diff options
author | Roland Scheidegger <[email protected]> | 2006-03-01 23:11:14 +0000 |
---|---|---|
committer | Roland Scheidegger <[email protected]> | 2006-03-01 23:11:14 +0000 |
commit | 93da673904d4c520d3fbd2210e53777bf1598ac6 (patch) | |
tree | 74245a84790c47d6a37bc2c15f57d86398918695 | |
parent | cf6be2d5bd7032d120424f8bc4c926d9d137d223 (diff) |
Fix glGet with enums which are used not only in NV_vertex_program, but other extensions too (ARB_vertex_program, ARB_fragment_program, NV_fragment_program), if no support for NV_vertex_program is present. While here, fix return value if enum is used which is not supported by the exposed extensions (GL_INVALID_ENUM instead of GL_INVALID_VALUE). Fix some compile-time #ifs depending on NV_vertex/fragment_program which should also be dependant on ARB_vertex/fragment_program. See bug #6070
-rw-r--r-- | src/mesa/main/enable.c | 8 | ||||
-rw-r--r-- | src/mesa/main/get.c | 77 | ||||
-rw-r--r-- | src/mesa/main/get_gen.py | 62 | ||||
-rw-r--r-- | src/mesa/shader/program.c | 4 |
4 files changed, 104 insertions, 47 deletions
diff --git a/src/mesa/main/enable.c b/src/mesa/main/enable.c index 931aa2cc26c..5efbf46a755 100644 --- a/src/mesa/main/enable.c +++ b/src/mesa/main/enable.c @@ -821,7 +821,7 @@ void _mesa_set_enable( GLcontext *ctx, GLenum cap, GLboolean state ) ctx->Point.PointSprite = state; break; -#if FEATURE_NV_vertex_program +#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program case GL_VERTEX_PROGRAM_NV: CHECK_EXTENSION2(NV_vertex_program, ARB_vertex_program, cap); if (ctx->VertexProgram.Enabled == state) @@ -843,6 +843,8 @@ void _mesa_set_enable( GLcontext *ctx, GLenum cap, GLboolean state ) FLUSH_VERTICES(ctx, _NEW_PROGRAM); ctx->VertexProgram.TwoSideEnabled = state; break; +#endif +#if FEATURE_NV_vertex_program case GL_MAP1_VERTEX_ATTRIB0_4_NV: case GL_MAP1_VERTEX_ATTRIB1_4_NV: case GL_MAP1_VERTEX_ATTRIB2_4_NV: @@ -1286,7 +1288,7 @@ _mesa_IsEnabled( GLenum cap ) case GL_POINT_SPRITE_NV: return ctx->Point.PointSprite; -#if FEATURE_NV_vertex_program +#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program case GL_VERTEX_PROGRAM_NV: CHECK_EXTENSION(NV_vertex_program); return ctx->VertexProgram.Enabled; @@ -1296,6 +1298,8 @@ _mesa_IsEnabled( GLenum cap ) case GL_VERTEX_PROGRAM_TWO_SIDE_NV: CHECK_EXTENSION(NV_vertex_program); return ctx->VertexProgram.TwoSideEnabled; +#endif +#if FEATURE_NV_vertex_program case GL_VERTEX_ATTRIB_ARRAY0_NV: case GL_VERTEX_ATTRIB_ARRAY1_NV: case GL_VERTEX_ATTRIB_ARRAY2_NV: diff --git a/src/mesa/main/get.c b/src/mesa/main/get.c index dcb8f8039f1..36084952e15 100644 --- a/src/mesa/main/get.c +++ b/src/mesa/main/get.c @@ -32,7 +32,7 @@ */ #define CHECK_EXT1(EXT1, FUNC) \ if (!ctx->Extensions.EXT1) { \ - _mesa_error(ctx, GL_INVALID_VALUE, FUNC "(0x%x)", (int) pname); \ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \ return; \ } @@ -41,10 +41,29 @@ */ #define CHECK_EXT2(EXT1, EXT2, FUNC) \ if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2) { \ - _mesa_error(ctx, GL_INVALID_VALUE, FUNC "(0x%x)", (int) pname); \ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \ return; \ } +/* + * Check if either of three extensions is enabled. + */ +#define CHECK_EXT3(EXT1, EXT2, EXT3, FUNC) \ + if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2 && \ + !ctx->Extensions.EXT3) { \ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \ + return; \ + } + +/* + * Check if either of four extensions is enabled. + */ +#define CHECK_EXT4(EXT1, EXT2, EXT3, EXT4, FUNC) \ + if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2 && \ + !ctx->Extensions.EXT3 && !ctx->Extensions.EXT4) { \ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \ + return; \ + } void GLAPIENTRY @@ -1390,27 +1409,27 @@ _mesa_GetBooleanv( GLenum pname, GLboolean *params ) params[0] = ctx->VertexProgram.Enabled; break; case GL_VERTEX_PROGRAM_POINT_SIZE_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT2(NV_vertex_program, ARB_vertex_program, "GetBooleanv"); params[0] = ctx->VertexProgram.PointSizeEnabled; break; case GL_VERTEX_PROGRAM_TWO_SIDE_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT2(NV_vertex_program, ARB_vertex_program, "GetBooleanv"); params[0] = ctx->VertexProgram.TwoSideEnabled; break; case GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetBooleanv"); params[0] = INT_TO_BOOLEAN(ctx->Const.MaxProgramMatrixStackDepth); break; case GL_MAX_TRACK_MATRICES_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetBooleanv"); params[0] = INT_TO_BOOLEAN(ctx->Const.MaxProgramMatrices); break; case GL_CURRENT_MATRIX_STACK_DEPTH_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetBooleanv"); params[0] = ctx->CurrentStack->Depth + 1; break; case GL_CURRENT_MATRIX_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetBooleanv"); { const GLfloat *matrix = ctx->CurrentStack->Top->m; params[0] = FLOAT_TO_BOOLEAN(matrix[0]); @@ -1436,7 +1455,7 @@ _mesa_GetBooleanv( GLenum pname, GLboolean *params ) params[0] = INT_TO_BOOLEAN((ctx->VertexProgram.Current ? ctx->VertexProgram.Current->Base.Id : 0)); break; case GL_PROGRAM_ERROR_POSITION_NV: - CHECK_EXT1(NV_vertex_program, "GetBooleanv"); + CHECK_EXT4(NV_vertex_program, ARB_vertex_program, NV_fragment_program, ARB_fragment_program, "GetBooleanv"); params[0] = INT_TO_BOOLEAN(ctx->Program.ErrorPos); break; case GL_VERTEX_ATTRIB_ARRAY0_NV: @@ -1572,11 +1591,11 @@ _mesa_GetBooleanv( GLenum pname, GLboolean *params ) params[0] = ctx->FragmentProgram.Enabled; break; case GL_MAX_TEXTURE_COORDS_NV: - CHECK_EXT1(NV_fragment_program, "GetBooleanv"); + CHECK_EXT2(NV_fragment_program, ARB_fragment_program, "GetBooleanv"); params[0] = INT_TO_BOOLEAN(ctx->Const.MaxTextureCoordUnits); break; case GL_MAX_TEXTURE_IMAGE_UNITS_NV: - CHECK_EXT1(NV_fragment_program, "GetBooleanv"); + CHECK_EXT2(NV_fragment_program, ARB_fragment_program, "GetBooleanv"); params[0] = INT_TO_BOOLEAN(ctx->Const.MaxTextureImageUnits); break; case GL_FRAGMENT_PROGRAM_BINDING_NV: @@ -3208,27 +3227,27 @@ _mesa_GetFloatv( GLenum pname, GLfloat *params ) params[0] = BOOLEAN_TO_FLOAT(ctx->VertexProgram.Enabled); break; case GL_VERTEX_PROGRAM_POINT_SIZE_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT2(NV_vertex_program, ARB_vertex_program, "GetFloatv"); params[0] = BOOLEAN_TO_FLOAT(ctx->VertexProgram.PointSizeEnabled); break; case GL_VERTEX_PROGRAM_TWO_SIDE_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT2(NV_vertex_program, ARB_vertex_program, "GetFloatv"); params[0] = BOOLEAN_TO_FLOAT(ctx->VertexProgram.TwoSideEnabled); break; case GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetFloatv"); params[0] = (GLfloat)(ctx->Const.MaxProgramMatrixStackDepth); break; case GL_MAX_TRACK_MATRICES_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetFloatv"); params[0] = (GLfloat)(ctx->Const.MaxProgramMatrices); break; case GL_CURRENT_MATRIX_STACK_DEPTH_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetFloatv"); params[0] = BOOLEAN_TO_FLOAT(ctx->CurrentStack->Depth + 1); break; case GL_CURRENT_MATRIX_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetFloatv"); { const GLfloat *matrix = ctx->CurrentStack->Top->m; params[0] = matrix[0]; @@ -3254,7 +3273,7 @@ _mesa_GetFloatv( GLenum pname, GLfloat *params ) params[0] = (GLfloat)((ctx->VertexProgram.Current ? ctx->VertexProgram.Current->Base.Id : 0)); break; case GL_PROGRAM_ERROR_POSITION_NV: - CHECK_EXT1(NV_vertex_program, "GetFloatv"); + CHECK_EXT4(NV_vertex_program, ARB_vertex_program, NV_fragment_program, ARB_fragment_program, "GetFloatv"); params[0] = (GLfloat)(ctx->Program.ErrorPos); break; case GL_VERTEX_ATTRIB_ARRAY0_NV: @@ -3390,11 +3409,11 @@ _mesa_GetFloatv( GLenum pname, GLfloat *params ) params[0] = BOOLEAN_TO_FLOAT(ctx->FragmentProgram.Enabled); break; case GL_MAX_TEXTURE_COORDS_NV: - CHECK_EXT1(NV_fragment_program, "GetFloatv"); + CHECK_EXT2(NV_fragment_program, ARB_fragment_program, "GetFloatv"); params[0] = (GLfloat)(ctx->Const.MaxTextureCoordUnits); break; case GL_MAX_TEXTURE_IMAGE_UNITS_NV: - CHECK_EXT1(NV_fragment_program, "GetFloatv"); + CHECK_EXT2(NV_fragment_program, ARB_fragment_program, "GetFloatv"); params[0] = (GLfloat)(ctx->Const.MaxTextureImageUnits); break; case GL_FRAGMENT_PROGRAM_BINDING_NV: @@ -5026,27 +5045,27 @@ _mesa_GetIntegerv( GLenum pname, GLint *params ) params[0] = BOOLEAN_TO_INT(ctx->VertexProgram.Enabled); break; case GL_VERTEX_PROGRAM_POINT_SIZE_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT2(NV_vertex_program, ARB_vertex_program, "GetIntegerv"); params[0] = BOOLEAN_TO_INT(ctx->VertexProgram.PointSizeEnabled); break; case GL_VERTEX_PROGRAM_TWO_SIDE_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT2(NV_vertex_program, ARB_vertex_program, "GetIntegerv"); params[0] = BOOLEAN_TO_INT(ctx->VertexProgram.TwoSideEnabled); break; case GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetIntegerv"); params[0] = ctx->Const.MaxProgramMatrixStackDepth; break; case GL_MAX_TRACK_MATRICES_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetIntegerv"); params[0] = ctx->Const.MaxProgramMatrices; break; case GL_CURRENT_MATRIX_STACK_DEPTH_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetIntegerv"); params[0] = BOOLEAN_TO_INT(ctx->CurrentStack->Depth + 1); break; case GL_CURRENT_MATRIX_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT3(NV_vertex_program, ARB_vertex_program, ARB_fragment_program, "GetIntegerv"); { const GLfloat *matrix = ctx->CurrentStack->Top->m; params[0] = IROUND(matrix[0]); @@ -5072,7 +5091,7 @@ _mesa_GetIntegerv( GLenum pname, GLint *params ) params[0] = (ctx->VertexProgram.Current ? ctx->VertexProgram.Current->Base.Id : 0); break; case GL_PROGRAM_ERROR_POSITION_NV: - CHECK_EXT1(NV_vertex_program, "GetIntegerv"); + CHECK_EXT4(NV_vertex_program, ARB_vertex_program, NV_fragment_program, ARB_fragment_program, "GetIntegerv"); params[0] = ctx->Program.ErrorPos; break; case GL_VERTEX_ATTRIB_ARRAY0_NV: @@ -5208,11 +5227,11 @@ _mesa_GetIntegerv( GLenum pname, GLint *params ) params[0] = BOOLEAN_TO_INT(ctx->FragmentProgram.Enabled); break; case GL_MAX_TEXTURE_COORDS_NV: - CHECK_EXT1(NV_fragment_program, "GetIntegerv"); + CHECK_EXT2(NV_fragment_program, ARB_fragment_program, "GetIntegerv"); params[0] = ctx->Const.MaxTextureCoordUnits; break; case GL_MAX_TEXTURE_IMAGE_UNITS_NV: - CHECK_EXT1(NV_fragment_program, "GetIntegerv"); + CHECK_EXT2(NV_fragment_program, ARB_fragment_program, "GetIntegerv"); params[0] = ctx->Const.MaxTextureImageUnits; break; case GL_FRAGMENT_PROGRAM_BINDING_NV: diff --git a/src/mesa/main/get_gen.py b/src/mesa/main/get_gen.py index cf1e94334c3..76adcf87721 100644 --- a/src/mesa/main/get_gen.py +++ b/src/mesa/main/get_gen.py @@ -704,29 +704,36 @@ StateVars = [ # GL_NV_vertex_program ( "GL_VERTEX_PROGRAM_NV", GLboolean, - ["ctx->VertexProgram.Enabled"], "", ["NV_vertex_program", "ARB_vertex_program"] ), + ["ctx->VertexProgram.Enabled"], "", + ["NV_vertex_program", "ARB_vertex_program"] ), ( "GL_VERTEX_PROGRAM_POINT_SIZE_NV", GLboolean, - ["ctx->VertexProgram.PointSizeEnabled"], "", ["NV_vertex_program"] ), + ["ctx->VertexProgram.PointSizeEnabled"], "", + ["NV_vertex_program", "ARB_vertex_program"] ), ( "GL_VERTEX_PROGRAM_TWO_SIDE_NV", GLboolean, - ["ctx->VertexProgram.TwoSideEnabled"], "", ["NV_vertex_program"] ), + ["ctx->VertexProgram.TwoSideEnabled"], "", + ["NV_vertex_program", "ARB_vertex_program"] ), ( "GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV", GLint, - ["ctx->Const.MaxProgramMatrixStackDepth"], "", ["NV_vertex_program"] ), + ["ctx->Const.MaxProgramMatrixStackDepth"], "", + ["NV_vertex_program", "ARB_vertex_program", "ARB_fragment_program"] ), ( "GL_MAX_TRACK_MATRICES_NV", GLint, - ["ctx->Const.MaxProgramMatrices"], "", ["NV_vertex_program"] ), + ["ctx->Const.MaxProgramMatrices"], "", + ["NV_vertex_program", "ARB_vertex_program", "ARB_fragment_program"] ), ( "GL_CURRENT_MATRIX_STACK_DEPTH_NV", GLboolean, - ["ctx->CurrentStack->Depth + 1"], "", ["NV_vertex_program"] ), + ["ctx->CurrentStack->Depth + 1"], "", + ["NV_vertex_program", "ARB_vertex_program", "ARB_fragment_program"] ), ( "GL_CURRENT_MATRIX_NV", GLfloat, ["matrix[0]", "matrix[1]", "matrix[2]", "matrix[3]", "matrix[4]", "matrix[5]", "matrix[6]", "matrix[7]", "matrix[8]", "matrix[9]", "matrix[10]", "matrix[11]", "matrix[12]", "matrix[13]", "matrix[14]", "matrix[15]" ], "const GLfloat *matrix = ctx->CurrentStack->Top->m;", - ["NV_vertex_program"] ), + ["NV_vertex_program", "ARB_vertex_program", "ARB_fragment_program"] ), ( "GL_VERTEX_PROGRAM_BINDING_NV", GLint, ["(ctx->VertexProgram.Current ? ctx->VertexProgram.Current->Base.Id : 0)"], "", ["NV_vertex_program"] ), ( "GL_PROGRAM_ERROR_POSITION_NV", GLint, - ["ctx->Program.ErrorPos"], "", ["NV_vertex_program"] ), + ["ctx->Program.ErrorPos"], "", ["NV_vertex_program", + "ARB_vertex_program", "NV_fragment_program", "ARB_fragment_program"] ), ( "GL_VERTEX_ATTRIB_ARRAY0_NV", GLboolean, ["ctx->Array.VertexAttrib[0].Enabled"], "", ["NV_vertex_program"] ), ( "GL_VERTEX_ATTRIB_ARRAY1_NV", GLboolean, @@ -796,9 +803,11 @@ StateVars = [ ( "GL_FRAGMENT_PROGRAM_NV", GLboolean, ["ctx->FragmentProgram.Enabled"], "", ["NV_fragment_program"] ), ( "GL_MAX_TEXTURE_COORDS_NV", GLint, - ["ctx->Const.MaxTextureCoordUnits"], "", ["NV_fragment_program"] ), + ["ctx->Const.MaxTextureCoordUnits"], "", + ["NV_fragment_program", "ARB_fragment_program"] ), ( "GL_MAX_TEXTURE_IMAGE_UNITS_NV", GLint, - ["ctx->Const.MaxTextureImageUnits"], "", ["NV_fragment_program"] ), + ["ctx->Const.MaxTextureImageUnits"], "", + ["NV_fragment_program", "ARB_fragment_program"] ), ( "GL_FRAGMENT_PROGRAM_BINDING_NV", GLint, ["ctx->FragmentProgram.Current ? ctx->FragmentProgram.Current->Base.Id : 0"], "", ["NV_fragment_program"] ), @@ -1039,10 +1048,16 @@ def EmitGetFunction(stateVars, returnType): if len(extensions) == 1: print (' CHECK_EXT1(%s, "%s");' % (extensions[0], function)) - else: - assert len(extensions) == 2 + elif len(extensions) == 2: print (' CHECK_EXT2(%s, %s, "%s");' % (extensions[0], extensions[1], function)) + elif len(extensions) == 3: + print (' CHECK_EXT3(%s, %s, %s, "%s");' % + (extensions[0], extensions[1], extensions[2], function)) + else: + assert len(extensions) == 4 + print (' CHECK_EXT4(%s, %s, %s, %s, "%s");' % + (extensions[0], extensions[1], extensions[2], extensions[3], function)) if optionalCode: print " {" print " " + optionalCode @@ -1102,7 +1117,7 @@ def EmitHeader(): */ #define CHECK_EXT1(EXT1, FUNC) \\ if (!ctx->Extensions.EXT1) { \\ - _mesa_error(ctx, GL_INVALID_VALUE, FUNC "(0x%x)", (int) pname); \\ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \\ return; \\ } @@ -1111,10 +1126,29 @@ def EmitHeader(): */ #define CHECK_EXT2(EXT1, EXT2, FUNC) \\ if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2) { \\ - _mesa_error(ctx, GL_INVALID_VALUE, FUNC "(0x%x)", (int) pname); \\ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \\ return; \\ } +/* + * Check if either of three extensions is enabled. + */ +#define CHECK_EXT3(EXT1, EXT2, EXT3, FUNC) \\ + if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2 && \\ + !ctx->Extensions.EXT3) { \\ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \\ + return; \\ + } + +/* + * Check if either of four extensions is enabled. + */ +#define CHECK_EXT4(EXT1, EXT2, EXT3, EXT4, FUNC) \\ + if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2 && \\ + !ctx->Extensions.EXT3 && !ctx->Extensions.EXT4) { \\ + _mesa_error(ctx, GL_INVALID_ENUM, FUNC "(0x%x)", (int) pname); \\ + return; \\ + } """ return diff --git a/src/mesa/shader/program.c b/src/mesa/shader/program.c index 4e8dacea7e3..a899618d148 100644 --- a/src/mesa/shader/program.c +++ b/src/mesa/shader/program.c @@ -107,14 +107,14 @@ _mesa_init_program(GLcontext *ctx) void _mesa_free_program_data(GLcontext *ctx) { -#if FEATURE_NV_vertex_program +#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program if (ctx->VertexProgram.Current) { ctx->VertexProgram.Current->Base.RefCount--; if (ctx->VertexProgram.Current->Base.RefCount <= 0) ctx->Driver.DeleteProgram(ctx, &(ctx->VertexProgram.Current->Base)); } #endif -#if FEATURE_NV_fragment_program +#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program if (ctx->FragmentProgram.Current) { ctx->FragmentProgram.Current->Base.RefCount--; if (ctx->FragmentProgram.Current->Base.RefCount <= 0) |