00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "cl_cinematic_roq.h"
00028 #include "cl_cinematic.h"
00029 #include "../renderer/r_draw.h"
00030 #include "../sound/s_main.h"
00031 #include "../sound/s_music.h"
00032
00033 typedef struct {
00034 int vr[256];
00035 int ug[256];
00036 int vg[256];
00037 int ub[256];
00038 } yuvTable_t;
00039
00040 #define ROQ_IDENT 0x1084
00041
00042 #define ROQ_QUAD_INFO 0x1001
00043 #define ROQ_QUAD_CODEBOOK 0x1002
00044 #define ROQ_QUAD_VQ 0x1011
00045 #define ROQ_SOUND_MONO 0x1020
00046 #define ROQ_SOUND_STEREO 0x1021
00047
00048 #define ROQ_CHUNK_HEADER_SIZE 8
00049
00050 #define ROQ_MAX_CHUNK_SIZE 65536
00051
00052 #define ROQ_SOUND_RATE 22050
00053
00054 #define ROQ_ID_FCC 0x4000
00055 #define ROQ_ID_SLD 0x8000
00056 #define ROQ_ID_CCC 0xC000
00057
00058 typedef struct {
00059 unsigned short id;
00060 unsigned int size;
00061 unsigned short flags;
00062 } roqChunk_t;
00063
00064 typedef struct {
00065 unsigned int pixel[4];
00066 } roqQuadVector_t;
00067
00068 typedef struct {
00069 byte index[4];
00070 } roqQuadCell_t;
00071
00072 typedef struct {
00073 qFILE file;
00074 int size;
00075 int offset;
00076
00077 int frameWidth;
00078 int frameHeight;
00079 int frameRate;
00080 byte * frameBuffer[2];
00081
00082 int startTime;
00083 int currentFrame;
00084
00085 int soundChannel;
00087 byte data[ROQ_MAX_CHUNK_SIZE + ROQ_CHUNK_HEADER_SIZE];
00088 byte * header;
00089
00090 roqChunk_t chunk;
00091 roqQuadVector_t quadVectors[256];
00092 roqQuadCell_t quadCells[256];
00093
00094 musicStream_t musicStream;
00095 } roqCinematic_t;
00096
00097 static short roqCin_sqrTable[256];
00098 static yuvTable_t roqCin_yuvTable;
00099
00100 static int roqCin_quadOffsets2[2][4];
00101 static int roqCin_quadOffsets4[2][4];
00102
00103 #define ROQCIN (*((roqCinematic_t*)cin->codecData))
00104
00108 static inline byte CIN_ROQ_ClampByte (int value)
00109 {
00110 if (value < 0)
00111 return 0;
00112
00113 if (value > 255)
00114 return 255;
00115
00116 return value;
00117 }
00118
00122 static void CIN_ROQ_ApplyVector2x2 (cinematic_t *cin, int x, int y, const byte *indices)
00123 {
00124 int i;
00125
00126 for (i = 0; i < 4; i++) {
00127 const int xp = x + roqCin_quadOffsets2[0][i];
00128 const int yp = y + roqCin_quadOffsets2[1][i];
00129 const unsigned int *src = (const unsigned int *)ROQCIN.quadVectors + (indices[i] * 4);
00130 unsigned int *dst = (unsigned int *)ROQCIN.frameBuffer[0] + (yp * ROQCIN.frameWidth + xp);
00131
00132 dst[0] = src[0];
00133 dst[1] = src[1];
00134
00135 dst += ROQCIN.frameWidth;
00136
00137 dst[0] = src[2];
00138 dst[1] = src[3];
00139 }
00140 }
00141
00145 static void CIN_ROQ_ApplyVector4x4 (cinematic_t *cin, int x, int y, const byte *indices)
00146 {
00147 int i;
00148
00149 for (i = 0; i < 4; i++) {
00150 const int xp = x + roqCin_quadOffsets4[0][i];
00151 const int yp = y + roqCin_quadOffsets4[1][i];
00152 const unsigned int *src = (const unsigned int *)ROQCIN.quadVectors + (indices[i] * 4);
00153 unsigned int *dst = (unsigned int *)ROQCIN.frameBuffer[0] + (yp * ROQCIN.frameWidth + xp);
00154
00155 dst[0] = src[0];
00156 dst[1] = src[0];
00157 dst[2] = src[1];
00158 dst[3] = src[1];
00159
00160 dst += ROQCIN.frameWidth;
00161
00162 dst[0] = src[0];
00163 dst[1] = src[0];
00164 dst[2] = src[1];
00165 dst[3] = src[1];
00166
00167 dst += ROQCIN.frameWidth;
00168
00169 dst[0] = src[2];
00170 dst[1] = src[2];
00171 dst[2] = src[3];
00172 dst[3] = src[3];
00173
00174 dst += ROQCIN.frameWidth;
00175
00176 dst[0] = src[2];
00177 dst[1] = src[2];
00178 dst[2] = src[3];
00179 dst[3] = src[3];
00180 }
00181 }
00182
00186 static void CIN_ROQ_ApplyMotion4x4 (cinematic_t *cin, int x, int y, int mx, int my, int mv)
00187 {
00188 int i;
00189 const int xp = x + 8 - (mv >> 4) - mx;
00190 const int yp = y + 8 - (mv & 15) - my;
00191 const unsigned int *src = (const unsigned int *)ROQCIN.frameBuffer[1] + (yp * ROQCIN.frameWidth + xp);
00192 unsigned int *dst = (unsigned int *)ROQCIN.frameBuffer[0] + (y * ROQCIN.frameWidth + x);
00193
00194 for (i = 0; i < 4; i++, src += ROQCIN.frameWidth, dst += ROQCIN.frameWidth) {
00195 dst[0] = src[0];
00196 dst[1] = src[1];
00197 dst[2] = src[2];
00198 dst[3] = src[3];
00199 }
00200 }
00201
00205 static void CIN_ROQ_ApplyMotion8x8 (cinematic_t *cin, int x, int y, int mx, int my, int mv)
00206 {
00207 int i;
00208 const int xp = x + 8 - (mv >> 4) - mx;
00209 const int yp = y + 8 - (mv & 15) - my;
00210 const unsigned int *src = (const unsigned int *)ROQCIN.frameBuffer[1] + (yp * ROQCIN.frameWidth + xp);
00211 unsigned int *dst = (unsigned int *)ROQCIN.frameBuffer[0] + (y * ROQCIN.frameWidth + x);
00212
00213 for (i = 0; i < 8; i++, src += ROQCIN.frameWidth, dst += ROQCIN.frameWidth) {
00214 dst[0] = src[0];
00215 dst[1] = src[1];
00216 dst[2] = src[2];
00217 dst[3] = src[3];
00218 dst[4] = src[4];
00219 dst[5] = src[5];
00220 dst[6] = src[6];
00221 dst[7] = src[7];
00222 }
00223 }
00224
00228 static void CIN_ROQ_DecodeInfo (cinematic_t *cin, const byte *data)
00229 {
00230 if (ROQCIN.frameBuffer[0] && ROQCIN.frameBuffer[1])
00231 return;
00232
00233
00234 ROQCIN.frameWidth = data[0] | (data[1] << 8);
00235 ROQCIN.frameHeight = data[2] | (data[3] << 8);
00236
00237 ROQCIN.frameWidth = LittleShort(ROQCIN.frameWidth);
00238 ROQCIN.frameHeight = LittleShort(ROQCIN.frameHeight);
00239
00240 if (!Q_IsPowerOfTwo(ROQCIN.frameWidth) || !Q_IsPowerOfTwo(ROQCIN.frameHeight))
00241 Com_Error(ERR_DROP, "CIN_DecodeInfo: size is not a power of two (%i x %i)", ROQCIN.frameWidth, ROQCIN.frameHeight);
00242
00243 ROQCIN.frameBuffer[0] = (byte *)Mem_PoolAlloc(ROQCIN.frameWidth * ROQCIN.frameHeight * 4, cl_genericPool, 0);
00244 ROQCIN.frameBuffer[1] = (byte *)Mem_PoolAlloc(ROQCIN.frameWidth * ROQCIN.frameHeight * 4, cl_genericPool, 0);
00245 }
00246
00250 static void CIN_ROQ_DecodeCodeBook (cinematic_t *cin, const byte *data)
00251 {
00252 int numQuadVectors, numQuadCells;
00253 int i;
00254
00255 if (ROQCIN.chunk.flags) {
00256 numQuadVectors = (ROQCIN.chunk.flags >> 8) & 0xFF;
00257 numQuadCells = (ROQCIN.chunk.flags >> 0) & 0xFF;
00258
00259 if (!numQuadVectors)
00260 numQuadVectors = 256;
00261 } else {
00262 numQuadVectors = 256;
00263 numQuadCells = 256;
00264 }
00265
00266
00267 for (i = 0; i < numQuadVectors; i++) {
00268 const int r = roqCin_yuvTable.vr[data[5]];
00269 const int g = roqCin_yuvTable.ug[data[4]] + roqCin_yuvTable.vg[data[5]];
00270 const int b = roqCin_yuvTable.ub[data[4]];
00271
00272 ((byte *)&ROQCIN.quadVectors[i].pixel[0])[0] = CIN_ROQ_ClampByte(data[0] + r);
00273 ((byte *)&ROQCIN.quadVectors[i].pixel[0])[1] = CIN_ROQ_ClampByte(data[0] - g);
00274 ((byte *)&ROQCIN.quadVectors[i].pixel[0])[2] = CIN_ROQ_ClampByte(data[0] + b);
00275 ((byte *)&ROQCIN.quadVectors[i].pixel[0])[3] = 255;
00276
00277 ((byte *)&ROQCIN.quadVectors[i].pixel[1])[0] = CIN_ROQ_ClampByte(data[1] + r);
00278 ((byte *)&ROQCIN.quadVectors[i].pixel[1])[1] = CIN_ROQ_ClampByte(data[1] - g);
00279 ((byte *)&ROQCIN.quadVectors[i].pixel[1])[2] = CIN_ROQ_ClampByte(data[1] + b);
00280 ((byte *)&ROQCIN.quadVectors[i].pixel[1])[3] = 255;
00281
00282 ((byte *)&ROQCIN.quadVectors[i].pixel[2])[0] = CIN_ROQ_ClampByte(data[2] + r);
00283 ((byte *)&ROQCIN.quadVectors[i].pixel[2])[1] = CIN_ROQ_ClampByte(data[2] - g);
00284 ((byte *)&ROQCIN.quadVectors[i].pixel[2])[2] = CIN_ROQ_ClampByte(data[2] + b);
00285 ((byte *)&ROQCIN.quadVectors[i].pixel[2])[3] = 255;
00286
00287 ((byte *)&ROQCIN.quadVectors[i].pixel[3])[0] = CIN_ROQ_ClampByte(data[3] + r);
00288 ((byte *)&ROQCIN.quadVectors[i].pixel[3])[1] = CIN_ROQ_ClampByte(data[3] - g);
00289 ((byte *)&ROQCIN.quadVectors[i].pixel[3])[2] = CIN_ROQ_ClampByte(data[3] + b);
00290 ((byte *)&ROQCIN.quadVectors[i].pixel[3])[3] = 255;
00291
00292 data += 6;
00293 }
00294
00295
00296 for (i = 0; i < numQuadCells; i++) {
00297 ROQCIN.quadCells[i].index[0] = data[0];
00298 ROQCIN.quadCells[i].index[1] = data[1];
00299 ROQCIN.quadCells[i].index[2] = data[2];
00300 ROQCIN.quadCells[i].index[3] = data[3];
00301
00302 data += 4;
00303 }
00304 }
00305
00309 static void CIN_ROQ_DecodeVideo (cinematic_t *cin, const byte *data)
00310 {
00311 byte *buffer;
00312 int vqFlag, vqFlagPos, vqCode;
00313 int xPos, yPos, xMot, yMot;
00314 int x, y;
00315 int index;
00316 int i;
00317
00318 if (!ROQCIN.frameBuffer[0] || !ROQCIN.frameBuffer[1])
00319 return;
00320
00321 vqFlag = 0;
00322 vqFlagPos = 0;
00323
00324 xPos = 0;
00325 yPos = 0;
00326
00327 xMot = (char)((ROQCIN.chunk.flags >> 8) & 0xFF);
00328 yMot = (char)((ROQCIN.chunk.flags >> 0) & 0xFF);
00329
00330 index = 0;
00331
00332 while (1) {
00333 for (y = yPos; y < yPos + 16; y += 8) {
00334 for (x = xPos; x < xPos + 16; x += 8) {
00335 if (!vqFlagPos) {
00336 vqFlagPos = 7;
00337 vqFlag = data[index + 0] | (data[index + 1] << 8);
00338 vqFlag = LittleShort(vqFlag);
00339 index += 2;
00340 } else
00341 vqFlagPos--;
00342
00343 vqCode = vqFlag & ROQ_ID_CCC;
00344 vqFlag <<= 2;
00345
00346 switch (vqCode) {
00347 case ROQ_ID_FCC:
00348 CIN_ROQ_ApplyMotion8x8(cin, x, y, xMot, yMot, data[index]);
00349 index += 1;
00350 break;
00351 case ROQ_ID_SLD:
00352 CIN_ROQ_ApplyVector4x4(cin, x, y, ROQCIN.quadCells[data[index]].index);
00353 index += 1;
00354 break;
00355 case ROQ_ID_CCC:
00356 for (i = 0; i < 4; i++) {
00357 const int xp = x + roqCin_quadOffsets4[0][i];
00358 const int yp = y + roqCin_quadOffsets4[1][i];
00359
00360 if (!vqFlagPos) {
00361 vqFlagPos = 7;
00362 vqFlag = data[index + 0] | (data[index + 1] << 8);
00363 vqFlag = LittleShort(vqFlag);
00364
00365 index += 2;
00366 } else
00367 vqFlagPos--;
00368
00369 vqCode = vqFlag & ROQ_ID_CCC;
00370 vqFlag <<= 2;
00371
00372 switch (vqCode) {
00373 case ROQ_ID_FCC:
00374 CIN_ROQ_ApplyMotion4x4(cin, xp, yp, xMot, yMot, data[index]);
00375 index += 1;
00376 break;
00377 case ROQ_ID_SLD:
00378 CIN_ROQ_ApplyVector2x2(cin, xp, yp, ROQCIN.quadCells[data[index]].index);
00379 index += 1;
00380 break;
00381 case ROQ_ID_CCC:
00382 CIN_ROQ_ApplyVector2x2(cin, xp, yp, &data[index]);
00383 index += 4;
00384 break;
00385 }
00386 }
00387 break;
00388 }
00389 }
00390 }
00391
00392 xPos += 16;
00393 if (xPos >= ROQCIN.frameWidth) {
00394 xPos -= ROQCIN.frameWidth;
00395
00396 yPos += 16;
00397 if (yPos >= ROQCIN.frameHeight)
00398 break;
00399 }
00400 }
00401
00402
00403 if (!ROQCIN.currentFrame)
00404 memcpy(ROQCIN.frameBuffer[1], ROQCIN.frameBuffer[0], ROQCIN.frameWidth * ROQCIN.frameHeight * 4);
00405 else {
00406 buffer = ROQCIN.frameBuffer[0];
00407 ROQCIN.frameBuffer[0] = ROQCIN.frameBuffer[1];
00408 ROQCIN.frameBuffer[1] = buffer;
00409 }
00410
00411 ROQCIN.currentFrame++;
00412 }
00413
00418 static void CIN_ROQ_DecodeSoundMono (cinematic_t *cin, const byte *data)
00419 {
00420 short samples[ROQ_MAX_CHUNK_SIZE * 2];
00421 int prev = 0;
00422 int i, j;
00423
00424 for (i = 0, j = 0; i < ROQCIN.chunk.size; i++, j += 2) {
00425 prev = (short)(prev + roqCin_sqrTable[data[i]]);
00426 samples[j] = (short)prev;
00427 samples[j + 1] = (short)prev;
00428 }
00429
00430 M_AddToSampleBuffer(&ROQCIN.musicStream, ROQ_SOUND_RATE, i, (const byte *)samples);
00431 }
00432
00437 static void CIN_ROQ_DecodeSoundStereo (cinematic_t *cin, const byte *data)
00438 {
00439 short samples[ROQ_MAX_CHUNK_SIZE];
00440 int i;
00441 short prevL = (ROQCIN.chunk.flags & 0xFF00) << 0;
00442 short prevR = (ROQCIN.chunk.flags & 0x00FF) << 8;
00443
00444 for (i = 0; i < ROQCIN.chunk.size; i += 2) {
00445 prevL = prevL + roqCin_sqrTable[data[i + 0]];
00446 prevR = prevR + roqCin_sqrTable[data[i + 1]];
00447
00448 samples[i + 0] = prevL;
00449 samples[i + 1] = prevR;
00450 }
00451
00452 M_AddToSampleBuffer(&ROQCIN.musicStream, ROQ_SOUND_RATE, i / 2, (const byte *)samples);
00453 }
00454
00459 static qboolean CIN_ROQ_DecodeChunk (cinematic_t *cin)
00460 {
00461 int frame;
00462
00463 if (ROQCIN.startTime + ((1000 / ROQCIN.frameRate) * ROQCIN.currentFrame) > CL_Milliseconds())
00464 return qtrue;
00465
00466 frame = ROQCIN.currentFrame;
00467
00468 do {
00469 if (ROQCIN.offset >= ROQCIN.size)
00470 return qfalse;
00471
00472
00473 ROQCIN.chunk.id = LittleShort(*(short *)&ROQCIN.header[0]);
00474 ROQCIN.chunk.size = LittleLong(*(int *)&ROQCIN.header[2]);
00475 ROQCIN.chunk.flags = LittleShort(*(short *)&ROQCIN.header[6]);
00476
00477 if (ROQCIN.chunk.id == ROQ_IDENT || ROQCIN.chunk.size > ROQ_MAX_CHUNK_SIZE) {
00478 Com_Printf("Invalid chunk id during decode: %i\n", ROQCIN.chunk.id);
00479 cin->replay = qfalse;
00480 return qfalse;
00481 }
00482
00483
00484 FS_Read(ROQCIN.data, ROQCIN.chunk.size + ROQ_CHUNK_HEADER_SIZE, &ROQCIN.file);
00485 ROQCIN.offset += ROQCIN.chunk.size + ROQ_CHUNK_HEADER_SIZE;
00486
00487 ROQCIN.header = ROQCIN.data + ROQCIN.chunk.size;
00488
00489
00490 switch (ROQCIN.chunk.id) {
00491 case ROQ_QUAD_INFO:
00492 CIN_ROQ_DecodeInfo(cin, ROQCIN.data);
00493 break;
00494 case ROQ_QUAD_CODEBOOK:
00495 CIN_ROQ_DecodeCodeBook(cin, ROQCIN.data);
00496 break;
00497 case ROQ_QUAD_VQ:
00498 CIN_ROQ_DecodeVideo(cin, ROQCIN.data);
00499 break;
00500 case ROQ_SOUND_MONO:
00501 if (!cin->noSound)
00502 CIN_ROQ_DecodeSoundMono(cin, ROQCIN.data);
00503 case ROQ_SOUND_STEREO:
00504 if (!cin->noSound)
00505 CIN_ROQ_DecodeSoundStereo(cin, ROQCIN.data);
00506 break;
00507 default:
00508 Com_Printf("Invalid chunk id: %i\n", ROQCIN.chunk.id);
00509 break;
00510 }
00511
00512 } while (frame == ROQCIN.currentFrame && cin->status);
00513
00514 return qtrue;
00515 }
00516
00520 static void CIN_ROQ_DrawCinematic (cinematic_t *cin)
00521 {
00522 int texnum;
00523
00524 assert(cin->status != CIN_STATUS_NONE);
00525
00526 if (!ROQCIN.frameBuffer[1])
00527 return;
00528 texnum = R_UploadData("***cinematic***", ROQCIN.frameBuffer[1], ROQCIN.frameWidth, ROQCIN.frameHeight);
00529 R_DrawTexture(texnum, cin->x, cin->y, cin->w, cin->h);
00530 }
00531
00535 qboolean CIN_ROQ_RunCinematic (cinematic_t *cin)
00536 {
00537 qboolean runState = CIN_ROQ_DecodeChunk(cin);
00538 if (runState)
00539 CIN_ROQ_DrawCinematic(cin);
00540 return runState;
00541 }
00542
00543 void CIN_ROQ_StopCinematic (cinematic_t *cin)
00544 {
00545 if (ROQCIN.file.f || ROQCIN.file.z)
00546 FS_CloseFile(&ROQCIN.file);
00547 else
00548 Com_Printf("CIN_ROQ_StopCinematic: Warning no opened file\n");
00549
00550 if (ROQCIN.frameBuffer[0]) {
00551 Mem_Free(ROQCIN.frameBuffer[0]);
00552 Mem_Free(ROQCIN.frameBuffer[1]);
00553 }
00554
00555 M_StopMusicStream(&ROQCIN.musicStream);
00556
00557 Mem_Free(cin->codecData);
00558 cin->codecData = NULL;
00559 }
00560
00561 void CIN_ROQ_PlayCinematic (cinematic_t *cin, const char *fileName)
00562 {
00563 roqChunk_t chunk;
00564 int size;
00565 byte header[ROQ_CHUNK_HEADER_SIZE];
00566
00567 assert(cin->codecData);
00568 cin->codecData = Mem_PoolAlloc(sizeof(ROQCIN), vid_genericPool, 0);
00569 memset(&ROQCIN, 0, sizeof(ROQCIN));
00570
00571
00572 size = FS_OpenFile(fileName, &ROQCIN.file, FILE_READ);
00573 if (!ROQCIN.file.f && !ROQCIN.file.z) {
00574 Com_Printf("Cinematic %s not found\n", fileName);
00575 return;
00576 }
00577
00578
00579 FS_Read(header, sizeof(header), &ROQCIN.file);
00580
00581
00582 chunk.id = LittleShort(*(short *)&header[0]);
00583 chunk.size = LittleLong(*(int *)&header[2]);
00584 chunk.flags = LittleShort(*(short *)&header[6]);
00585
00586 if (chunk.id != ROQ_IDENT) {
00587 FS_CloseFile(&ROQCIN.file);
00588 Com_Error(ERR_DROP, "CIN_PlayCinematic: invalid RoQ header");
00589 }
00590
00591 cin->cinematicType = CINEMATIC_TYPE_ROQ;
00592
00593
00594 Q_strncpyz(cin->name, fileName, sizeof(cin->name));
00595
00596 M_PlayMusicStream(&ROQCIN.musicStream);
00597
00598 ROQCIN.size = size;
00599 ROQCIN.offset = sizeof(header);
00600
00601 ROQCIN.frameWidth = 0;
00602 ROQCIN.frameHeight = 0;
00603 ROQCIN.startTime = CL_Milliseconds();
00604 ROQCIN.frameRate = (chunk.flags != 0) ? chunk.flags : 30;
00605 if (ROQCIN.frameBuffer[0]) {
00606 Mem_Free(ROQCIN.frameBuffer[0]);
00607 ROQCIN.frameBuffer[0] = NULL;
00608 }
00609 if (ROQCIN.frameBuffer[1]) {
00610 Mem_Free(ROQCIN.frameBuffer[1]);
00611 ROQCIN.frameBuffer[1] = NULL;
00612 }
00613
00614 ROQCIN.currentFrame = 0;
00615
00616
00617 FS_Read(ROQCIN.data, ROQ_CHUNK_HEADER_SIZE, &ROQCIN.file);
00618 ROQCIN.offset += ROQ_CHUNK_HEADER_SIZE;
00619
00620 ROQCIN.header = ROQCIN.data;
00621 }
00622
00623 void CIN_ROQ_Init (void)
00624 {
00625 int i;
00626
00627
00628 for (i = 0; i < 128; i++) {
00629 const short s = (short)(i * i);
00630 roqCin_sqrTable[i] = s;
00631 roqCin_sqrTable[i + 128] = -s;
00632 }
00633
00634
00635 for (i = 0; i < 4; i++) {
00636 roqCin_quadOffsets2[0][i] = 2 * (i & 1);
00637 roqCin_quadOffsets2[1][i] = 2 * (i >> 1);
00638 roqCin_quadOffsets4[0][i] = 4 * (i & 1);
00639 roqCin_quadOffsets4[1][i] = 4 * (i >> 1);
00640 }
00641
00642
00643 for (i = 0; i < 256; i++) {
00644 const float f = (float)(i - 128);
00645 roqCin_yuvTable.vr[i] = Q_ftol(f * 1.40200f);
00646 roqCin_yuvTable.ug[i] = Q_ftol(f * 0.34414f);
00647 roqCin_yuvTable.vg[i] = Q_ftol(f * 0.71414f);
00648 roqCin_yuvTable.ub[i] = Q_ftol(f * 1.77200f);
00649 }
00650 }