Glide Plugin GLES2 port from mupen64plus-ae, but with special FrameSkip code
[mupen64plus-pandora.git] / source / gles2glide64 / src / Glitch64 / geometry.cpp
CommitLineData
98e75f2d 1/*
2* Glide64 - Glide video plugin for Nintendo 64 emulators.
3* Copyright (c) 2002 Dave2001
4* Copyright (c) 2003-2009 Sergey 'Gonetz' Lipski
5*
6* This program is free software; you can redistribute it and/or modify
7* it under the terms of the GNU General Public License as published by
8* the Free Software Foundation; either version 2 of the License, or
9* any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program; if not, write to the Free Software
18* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19*/
20
21#include <stdio.h>
22#include <string.h>
23#ifdef _WIN32
24#include <windows.h>
25#endif // _WIN32
26#include "glide.h"
27#include "main.h"
28#include "../Glide64/winlnxdefs.h"
29#include "../Glide64/rdp.h"
30
31
32
33#define Z_MAX (65536.0f)
34#define VERTEX_SIZE sizeof(VERTEX) //Size of vertex struct
35
36#ifdef PAULSCODE
37//#include "ae_bridge.h"
38static float polygonOffsetFactor;
39static float polygonOffsetUnits;
40#endif
41
42static int xy_off;
43static int xy_en;
44static int z_en;
45static int z_off;
46static int q_off;
47static int q_en;
48static int pargb_off;
49static int pargb_en;
50static int st0_off;
51static int st0_en;
52static int st1_off;
53static int st1_en;
54static int fog_ext_off;
55static int fog_ext_en;
56
57int w_buffer_mode;
58int inverted_culling;
59int culling_mode;
60
61#define VERTEX_BUFFER_SIZE 1500 //Max amount of vertices to buffer, this seems large enough.
62static VERTEX vertex_buffer[VERTEX_BUFFER_SIZE];
63static int vertex_buffer_count = 0;
64static GLenum vertex_draw_mode;
65static bool vertex_buffer_enabled = false;
66
67void vbo_init()
68{
69
70}
71
72void vbo_draw()
73{
74 if(vertex_buffer_count)
75 {
76 glDrawArrays(vertex_draw_mode,0,vertex_buffer_count);
77 vertex_buffer_count = 0;
78 }
79}
80
81#ifdef PAULSCODE
82void vbo_resetcount()
83{
84 vertex_buffer_count = 0;
85}
86#endif
87
88//Buffer vertices instead of glDrawArrays(...)
89void vbo_buffer(GLenum mode,GLint first,GLsizei count,void* pointers)
90{
91 if((count != 3 && mode != GL_TRIANGLES) || vertex_buffer_count + count > VERTEX_BUFFER_SIZE)
92 {
93 vbo_draw();
94 }
95
96 memcpy(&vertex_buffer[vertex_buffer_count],pointers,count * VERTEX_SIZE);
97 vertex_buffer_count += count;
98
99 if(count == 3 || mode == GL_TRIANGLES)
100 {
101 vertex_draw_mode = GL_TRIANGLES;
102 }
103 else
104 {
105 vertex_draw_mode = mode;
106 vbo_draw(); //Triangle fans and strips can't be joined as easily, just draw them straight away.
107 }
108
109
110}
111
112void vbo_enable()
113{
114 if(vertex_buffer_enabled)
115 return;
116
117 vertex_buffer_enabled = true;
118 glEnableVertexAttribArray(POSITION_ATTR);
119 glVertexAttribPointer(POSITION_ATTR, 4, GL_FLOAT, false, VERTEX_SIZE, &vertex_buffer[0].x); //Position
120
121 glEnableVertexAttribArray(COLOUR_ATTR);
122 glVertexAttribPointer(COLOUR_ATTR, 4, GL_UNSIGNED_BYTE, true, VERTEX_SIZE, &vertex_buffer[0].b); //Colour
123
124 glEnableVertexAttribArray(TEXCOORD_0_ATTR);
125 glVertexAttribPointer(TEXCOORD_0_ATTR, 2, GL_FLOAT, false, VERTEX_SIZE, &vertex_buffer[0].coord[2]); //Tex0
126
127 glEnableVertexAttribArray(TEXCOORD_1_ATTR);
128 glVertexAttribPointer(TEXCOORD_1_ATTR, 2, GL_FLOAT, false, VERTEX_SIZE, &vertex_buffer[0].coord[0]); //Tex1
129
130 glEnableVertexAttribArray(FOG_ATTR);
131 glVertexAttribPointer(FOG_ATTR, 1, GL_FLOAT, false, VERTEX_SIZE, &vertex_buffer[0].f); //Fog
132}
133
134void vbo_disable()
135{
136 vbo_draw();
137 vertex_buffer_enabled = false;
138}
139
140
141inline float ZCALC(const float & z, const float & q) {
142 float res = z_en ? ((z) / Z_MAX) / (q) : 1.0f;
143 return res;
144}
145
146/*
147#define zclamp (1.0f-1.0f/zscale)
148static inline void zclamp_glVertex4f(float a, float b, float c, float d)
149{
150 if (c<zclamp) c = zclamp;
151 glVertex4f(a,b,c,d);
152}
153#define glVertex4f(a,b,c,d) zclamp_glVertex4f(a,b,c,d)
154*/
155
156
157static inline float ytex(int tmu, float y) {
158 if (invtex[tmu])
159 return invtex[tmu] - y;
160 else
161 return y;
162}
163
164void init_geometry()
165{
166 xy_en = q_en = pargb_en = st0_en = st1_en = z_en = 0;
167 w_buffer_mode = 0;
168 inverted_culling = 0;
169
170 glDisable(GL_CULL_FACE);
171 glDisable(GL_DEPTH_TEST);
172
173 vbo_init();
174}
175
176FX_ENTRY void FX_CALL
177grCoordinateSpace( GrCoordinateSpaceMode_t mode )
178{
179 LOG("grCoordinateSpace(%d)\r\n", mode);
180 switch(mode)
181 {
182 case GR_WINDOW_COORDS:
183 break;
184 default:
185 display_warning("unknwown coordinate space : %x", mode);
186 }
187}
188
189FX_ENTRY void FX_CALL
190grVertexLayout(FxU32 param, FxI32 offset, FxU32 mode)
191{
192 LOG("grVertexLayout(%d,%d,%d)\r\n", param, offset, mode);
193 switch(param)
194 {
195 case GR_PARAM_XY:
196 xy_en = mode;
197 xy_off = offset;
198 break;
199 case GR_PARAM_Z:
200 z_en = mode;
201 z_off = offset;
202 break;
203 case GR_PARAM_Q:
204 q_en = mode;
205 q_off = offset;
206 break;
207 case GR_PARAM_FOG_EXT:
208 fog_ext_en = mode;
209 fog_ext_off = offset;
210 break;
211 case GR_PARAM_PARGB:
212 pargb_en = mode;
213 pargb_off = offset;
214 break;
215 case GR_PARAM_ST0:
216 st0_en = mode;
217 st0_off = offset;
218 break;
219 case GR_PARAM_ST1:
220 st1_en = mode;
221 st1_off = offset;
222 break;
223 default:
224 display_warning("unknown grVertexLayout parameter : %x", param);
225 }
226}
227
228FX_ENTRY void FX_CALL
229grCullMode( GrCullMode_t mode )
230{
231 LOG("grCullMode(%d)\r\n", mode);
232 static int oldmode = -1, oldinv = -1;
233 culling_mode = mode;
234 if (inverted_culling == oldinv && oldmode == mode)
235 return;
236 oldmode = mode;
237 oldinv = inverted_culling;
238 switch(mode)
239 {
240 case GR_CULL_DISABLE:
241 glDisable(GL_CULL_FACE);
242 break;
243 case GR_CULL_NEGATIVE:
244 if (!inverted_culling)
245 glCullFace(GL_FRONT);
246 else
247 glCullFace(GL_BACK);
248 glEnable(GL_CULL_FACE);
249 break;
250 case GR_CULL_POSITIVE:
251 if (!inverted_culling)
252 glCullFace(GL_BACK);
253 else
254 glCullFace(GL_FRONT);
255 glEnable(GL_CULL_FACE);
256 break;
257 default:
258 display_warning("unknown cull mode : %x", mode);
259 }
260}
261
262// Depth buffer
263
264FX_ENTRY void FX_CALL
265grDepthBufferMode( GrDepthBufferMode_t mode )
266{
267 LOG("grDepthBufferMode(%d)\r\n", mode);
268 switch(mode)
269 {
270 case GR_DEPTHBUFFER_DISABLE:
271 glDisable(GL_DEPTH_TEST);
272 w_buffer_mode = 0;
273 return;
274 case GR_DEPTHBUFFER_WBUFFER:
275 case GR_DEPTHBUFFER_WBUFFER_COMPARE_TO_BIAS:
276 glEnable(GL_DEPTH_TEST);
277 w_buffer_mode = 1;
278 break;
279 case GR_DEPTHBUFFER_ZBUFFER:
280 case GR_DEPTHBUFFER_ZBUFFER_COMPARE_TO_BIAS:
281 glEnable(GL_DEPTH_TEST);
282 w_buffer_mode = 0;
283 break;
284 default:
285 display_warning("unknown depth buffer mode : %x", mode);
286 }
287}
288
289FX_ENTRY void FX_CALL
290grDepthBufferFunction( GrCmpFnc_t function )
291{
292 LOG("grDepthBufferFunction(%d)\r\n", function);
293 switch(function)
294 {
295 case GR_CMP_GEQUAL:
296 if (w_buffer_mode)
297 glDepthFunc(GL_LEQUAL);
298 else
299 glDepthFunc(GL_GEQUAL);
300 break;
301 case GR_CMP_LEQUAL:
302 if (w_buffer_mode)
303 glDepthFunc(GL_GEQUAL);
304 else
305 glDepthFunc(GL_LEQUAL);
306 break;
307 case GR_CMP_LESS:
308 if (w_buffer_mode)
309 glDepthFunc(GL_GREATER);
310 else
311 glDepthFunc(GL_LESS);
312 break;
313 case GR_CMP_ALWAYS:
314 glDepthFunc(GL_ALWAYS);
315 break;
316 case GR_CMP_EQUAL:
317 glDepthFunc(GL_EQUAL);
318 break;
319 case GR_CMP_GREATER:
320 if (w_buffer_mode)
321 glDepthFunc(GL_LESS);
322 else
323 glDepthFunc(GL_GREATER);
324 break;
325 case GR_CMP_NEVER:
326 glDepthFunc(GL_NEVER);
327 break;
328 case GR_CMP_NOTEQUAL:
329 glDepthFunc(GL_NOTEQUAL);
330 break;
331
332 default:
333 display_warning("unknown depth buffer function : %x", function);
334 }
335}
336
337FX_ENTRY void FX_CALL
338grDepthMask( FxBool mask )
339{
340 LOG("grDepthMask(%d)\r\n", mask);
341 glDepthMask(mask);
342}
343
344float biasFactor = 0;
345void FindBestDepthBias()
346{
347#ifdef PAULSCODE
348/* int hardwareType = Android_JNI_GetHardwareType();
349 Android_JNI_GetPolygonOffset(hardwareType, 1, &polygonOffsetFactor, &polygonOffsetUnits);*/
350// glPolygonOffset(0.2f, 0.2f);
351 polygonOffsetFactor=0.2f;
352 polygonOffsetUnits=0.2f;
353#else
354 float f, bestz = 0.25f;
355 int x;
356 if (biasFactor) return;
357 biasFactor = 64.0f; // default value
358 glPushAttrib(GL_ALL_ATTRIB_BITS);
359 glEnable(GL_DEPTH_TEST);
360 glDepthFunc(GL_ALWAYS);
361 glEnable(GL_POLYGON_OFFSET_FILL);
362 glDrawBuffer(GL_BACK);
363 glReadBuffer(GL_BACK);
364 glDisable(GL_BLEND);
365 glDisable(GL_ALPHA_TEST);
366 glColor4ub(255,255,255,255);
367 glDepthMask(GL_TRUE);
368 for (x=0, f=1.0f; f<=65536.0f; x+=4, f*=2.0f) {
369 float z;
370 glPolygonOffset(0, f);
371 glBegin(GL_TRIANGLE_STRIP);
372 glVertex3f(float(x+4 - widtho)/(width/2), float(0 - heighto)/(height/2), 0.5);
373 glVertex3f(float(x - widtho)/(width/2), float(0 - heighto)/(height/2), 0.5);
374 glVertex3f(float(x+4 - widtho)/(width/2), float(4 - heighto)/(height/2), 0.5);
375 glVertex3f(float(x - widtho)/(width/2), float(4 - heighto)/(height/2), 0.5);
376 glEnd();
377 glReadPixels(x+2, 2+viewport_offset, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
378 z -= 0.75f + 8e-6f;
379 if (z<0.0f) z = -z;
380 if (z > 0.01f) continue;
381 if (z < bestz) {
382 bestz = z;
383 biasFactor = f;
384 }
385 //printf("f %g z %g\n", f, z);
386 }
387 //printf(" --> bias factor %g\n", biasFactor);
388 glPopAttrib();
389#endif
390}
391
392FX_ENTRY void FX_CALL
393grDepthBiasLevel( FxI32 level )
394{
395 LOG("grDepthBiasLevel(%d)\r\n", level);
396 if (level)
397 {
398 #ifdef PAULSCODE
399 glPolygonOffset(polygonOffsetFactor, polygonOffsetUnits);
400/* if(w_buffer_mode)
401 glPolygonOffset(1.0f, -(float)level*polygonOffsetUnits);
402 else
403 glPolygonOffset(0, (float)level*3.0f);*/
404 #else
405 if(w_buffer_mode)
406 glPolygonOffset(1.0f, -(float)level*zscale/255.0f);
407 else
408 glPolygonOffset(0, (float)level*biasFactor);
409 #endif
410 glEnable(GL_POLYGON_OFFSET_FILL);
411 }
412 else
413 {
414 glPolygonOffset(0,0);
415 glDisable(GL_POLYGON_OFFSET_FILL);
416 }
417}
418
419// draw
420
421FX_ENTRY void FX_CALL
422grDrawTriangle( const void *a, const void *b, const void *c )
423{
424 LOG("grDrawTriangle()\r\n\t");
425/*
426 if(nvidia_viewport_hack && !render_to_texture)
427 {
428 glViewport(0, viewport_offset, viewport_width, viewport_height);
429 nvidia_viewport_hack = 0;
430 }
431*/
432 reloadTexture();
433
434 if(need_to_compile) compile_shader();
435
436 if(vertex_buffer_count + 3 > VERTEX_BUFFER_SIZE)
437 {
438 vbo_draw();
439 }
440 vertex_draw_mode = GL_TRIANGLES;
441 memcpy(&vertex_buffer[vertex_buffer_count],a,VERTEX_SIZE);
442 memcpy(&vertex_buffer[vertex_buffer_count+1],b,VERTEX_SIZE);
443 memcpy(&vertex_buffer[vertex_buffer_count+2],c,VERTEX_SIZE);
444 vertex_buffer_count += 3;
445}
446
447FX_ENTRY void FX_CALL
448grDrawPoint( const void *pt )
449{
450/*
451 float *x = (float*)pt + xy_off/sizeof(float);
452 float *y = (float*)pt + xy_off/sizeof(float) + 1;
453 float *z = (float*)pt + z_off/sizeof(float);
454 float *q = (float*)pt + q_off/sizeof(float);
455 unsigned char *pargb = (unsigned char*)pt + pargb_off;
456 float *s0 = (float*)pt + st0_off/sizeof(float);
457 float *t0 = (float*)pt + st0_off/sizeof(float) + 1;
458 float *s1 = (float*)pt + st1_off/sizeof(float);
459 float *t1 = (float*)pt + st1_off/sizeof(float) + 1;
460 float *fog = (float*)pt + fog_ext_off/sizeof(float);
461 LOG("grDrawPoint()\r\n");
462
463 if(nvidia_viewport_hack && !render_to_texture)
464 {
465 glViewport(0, viewport_offset, viewport_width, viewport_height);
466 nvidia_viewport_hack = 0;
467 }
468
469 reloadTexture();
470
471 if(need_to_compile) compile_shader();
472
473 glBegin(GL_POINTS);
474
475 if (nbTextureUnits > 2)
476 {
477 if (st0_en)
478 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *s0 / *q / (float)tex1_width,
479 ytex(0, *t0 / *q / (float)tex1_height));
480 if (st1_en)
481 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *s1 / *q / (float)tex0_width,
482 ytex(1, *t1 / *q / (float)tex0_height));
483 }
484 else
485 {
486 if (st0_en)
487 glTexCoord2f(*s0 / *q / (float)tex0_width,
488 ytex(0, *t0 / *q / (float)tex0_height));
489 }
490 if (pargb_en)
491 glColor4f(pargb[2]/255.0f, pargb[1]/255.0f, pargb[0]/255.0f, pargb[3]/255.0f);
492 if (fog_enabled && fog_coord_support)
493 {
494 if(!fog_ext_en || fog_enabled != 2)
495 glSecondaryColor3f((1.0f / *q) / 255.0f, 0.0f, 0.0f);
496 else
497 glSecondaryColor3f((1.0f / *fog) / 255.0f, 0.0f, 0.0f);
498 }
499 glVertex4f((*x - (float)widtho) / (float)(width/2) / *q,
500 -(*y - (float)heighto) / (float)(height/2) / *q, ZCALC(*z ,*q), 1.0f / *q);
501
502 glEnd();
503*/
504}
505
506FX_ENTRY void FX_CALL
507grDrawLine( const void *a, const void *b )
508{
509/*
510 float *a_x = (float*)a + xy_off/sizeof(float);
511 float *a_y = (float*)a + xy_off/sizeof(float) + 1;
512 float *a_z = (float*)a + z_off/sizeof(float);
513 float *a_q = (float*)a + q_off/sizeof(float);
514 unsigned char *a_pargb = (unsigned char*)a + pargb_off;
515 float *a_s0 = (float*)a + st0_off/sizeof(float);
516 float *a_t0 = (float*)a + st0_off/sizeof(float) + 1;
517 float *a_s1 = (float*)a + st1_off/sizeof(float);
518 float *a_t1 = (float*)a + st1_off/sizeof(float) + 1;
519 float *a_fog = (float*)a + fog_ext_off/sizeof(float);
520
521 float *b_x = (float*)b + xy_off/sizeof(float);
522 float *b_y = (float*)b + xy_off/sizeof(float) + 1;
523 float *b_z = (float*)b + z_off/sizeof(float);
524 float *b_q = (float*)b + q_off/sizeof(float);
525 unsigned char *b_pargb = (unsigned char*)b + pargb_off;
526 float *b_s0 = (float*)b + st0_off/sizeof(float);
527 float *b_t0 = (float*)b + st0_off/sizeof(float) + 1;
528 float *b_s1 = (float*)b + st1_off/sizeof(float);
529 float *b_t1 = (float*)b + st1_off/sizeof(float) + 1;
530 float *b_fog = (float*)b + fog_ext_off/sizeof(float);
531 LOG("grDrawLine()\r\n");
532
533 if(nvidia_viewport_hack && !render_to_texture)
534 {
535 glViewport(0, viewport_offset, viewport_width, viewport_height);
536 nvidia_viewport_hack = 0;
537 }
538
539 reloadTexture();
540
541 if(need_to_compile) compile_shader();
542
543 glBegin(GL_LINES);
544
545 if (nbTextureUnits > 2)
546 {
547 if (st0_en)
548 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *a_s0 / *a_q / (float)tex1_width, ytex(0, *a_t0 / *a_q / (float)tex1_height));
549 if (st1_en)
550 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *a_s1 / *a_q / (float)tex0_width, ytex(1, *a_t1 / *a_q / (float)tex0_height));
551 }
552 else
553 {
554 if (st0_en)
555 glTexCoord2f(*a_s0 / *a_q / (float)tex0_width, ytex(0, *a_t0 / *a_q / (float)tex0_height));
556 }
557 if (pargb_en)
558 glColor4f(a_pargb[2]/255.0f, a_pargb[1]/255.0f, a_pargb[0]/255.0f, a_pargb[3]/255.0f);
559 if (fog_enabled && fog_coord_support)
560 {
561 if(!fog_ext_en || fog_enabled != 2)
562 glSecondaryColor3f((1.0f / *a_q) / 255.0f, 0.0f, 0.0f);
563 else
564 glSecondaryColor3f((1.0f / *a_fog) / 255.0f, 0.0f, 0.0f);
565 }
566 glVertex4f((*a_x - (float)widtho) / (float)(width/2) / *a_q,
567 -(*a_y - (float)heighto) / (float)(height/2) / *a_q, ZCALC(*a_z, *a_q), 1.0f / *a_q);
568
569 if (nbTextureUnits > 2)
570 {
571 if (st0_en)
572 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *b_s0 / *b_q / (float)tex1_width,
573 ytex(0, *b_t0 / *b_q / (float)tex1_height));
574 if (st1_en)
575 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *b_s1 / *b_q / (float)tex0_width,
576 ytex(1, *b_t1 / *b_q / (float)tex0_height));
577 }
578 else
579 {
580 if (st0_en)
581 glTexCoord2f(*b_s0 / *b_q / (float)tex0_width,
582 ytex(0, *b_t0 / *b_q / (float)tex0_height));
583 }
584 if (pargb_en)
585 glColor4f(b_pargb[2]/255.0f, b_pargb[1]/255.0f, b_pargb[0]/255.0f, b_pargb[3]/255.0f);
586 if (fog_enabled && fog_coord_support)
587 {
588 if(!fog_ext_en || fog_enabled != 2)
589 glSecondaryColor3f((1.0f / *b_q) / 255.0f, 0.0f, 0.0f);
590 else
591 glSecondaryColor3f((1.0f / *b_fog) / 255.0f, 0.0f, 0.0f);
592 }
593 glVertex4f((*b_x - (float)widtho) / (float)(width/2) / *b_q,
594 -(*b_y - (float)heighto) / (float)(height/2) / *b_q, ZCALC(*b_z, *b_q), 1.0f / *b_q);
595
596 glEnd();
597*/
598}
599
600FX_ENTRY void FX_CALL
601grDrawVertexArray(FxU32 mode, FxU32 Count, void *pointers2)
602{
603 void **pointers = (void**)pointers2;
604 LOG("grDrawVertexArray(%d,%d)\r\n", mode, Count);
605/*
606 if(nvidia_viewport_hack && !render_to_texture)
607 {
608 glViewport(0, viewport_offset, viewport_width, viewport_height);
609 nvidia_viewport_hack = 0;
610 }
611*/
612 reloadTexture();
613
614 if(need_to_compile) compile_shader();
615
616 if(mode != GR_TRIANGLE_FAN)
617 {
618 display_warning("grDrawVertexArray : unknown mode : %x", mode);
619 }
620
621 vbo_enable();
622 vbo_buffer(GL_TRIANGLE_FAN,0,Count,pointers[0]);
623}
624
625FX_ENTRY void FX_CALL
626grDrawVertexArrayContiguous(FxU32 mode, FxU32 Count, void *pointers, FxU32 stride)
627{
628 LOG("grDrawVertexArrayContiguous(%d,%d,%d)\r\n", mode, Count, stride);
629/*
630 if(nvidia_viewport_hack && !render_to_texture)
631 {
632 glViewport(0, viewport_offset, viewport_width, viewport_height);
633 nvidia_viewport_hack = 0;
634 }
635*/
636 if(stride != 156)
637 {
638 LOGINFO("Incompatible stride\n");
639 }
640
641 reloadTexture();
642
643 if(need_to_compile) compile_shader();
644
645 vbo_enable();
646
647 switch(mode)
648 {
649 case GR_TRIANGLE_STRIP:
650 vbo_buffer(GL_TRIANGLE_STRIP,0,Count,pointers);
651 break;
652 case GR_TRIANGLE_FAN:
653 vbo_buffer(GL_TRIANGLE_FAN,0,Count,pointers);
654 break;
655 default:
656 display_warning("grDrawVertexArrayContiguous : unknown mode : %x", mode);
657 }
658}