1 /***************************************************************************
2                     tgf.h -- Interface file for The Gaming Framework
3                              -------------------
4     created              : Fri Aug 13 22:32:14 CEST 1999
5     copyright            : (C) 1999-2014 by Eric Espie, Bernhard Wymann
6     email                : torcs@free.fr
7     version              : $Id: tgf.h,v 1.41.2.10 2014/05/23 08:38:31 berniw Exp $
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 /** @file
20     The Gaming Framework API.
21     @author	Bernhard Wymann, Eric Espie
22     @version $Id: tgf.h,v 1.41.2.10 2014/05/23 08:38:31 berniw Exp $
23 */
24 
25 
26 
27 #ifndef __TGF__H__
28 #define __TGF__H__
29 
30 #include <stdio.h>
31 #ifndef WIN32
32 #include <sys/param.h>
33 #include <assert.h>
34 #endif /* WIN32 */
35 #include <stdlib.h>
36 #ifdef WIN32
37 #include <windows.h>
38 #endif
39 #include <stdarg.h>
40 #include <cstring>
41 #include <math.h>
42 #include <osspec.h>
43 
44 
45 /* typedef double tdble; */
46 /** Floating point type used in TORCS.
47     @ingroup definitions
48 */
49 typedef float tdble;
50 
51 extern void GfInit(void);
52 
53 #ifndef MAX
54 #define MAX(x,y) ((x) > (y) ? (x) : (y))
55 #endif
56 
57 #ifndef MIN
58 #define MIN(x,y) ((x) < (y) ? (x) : (y))
59 #endif
60 
61 #define FREEZ(x) do {				\
62     if (x) {					\
63 	free(x);				\
64 	x = 0;					\
65     }						\
66 } while (0)
67 
68 #define freez FREEZ
69 
70 const double PI = 3.14159265358979323846;  /**< PI */
71 const tdble G = 9.80665f; /**< m/s/s */
72 
73 /* conversion */
74 #define RADS2RPM(x) ((x)*9.549296585)		/**< Radian/s to RPM conversion */
75 #define RPM2RADS(x) ((x)*.104719755)		/**< RPM to Radian/s conversion */
76 #define RAD2DEG(x)  ((x)*(180.0/PI))		/**< Radian to degree conversion */
77 #define DEG2RAD(x)  ((x)*(PI/180.0))		/**< Degree to radian conversion */
78 #define FEET2M(x)   ((x)*0.304801)		/**< Feet to meter conversion */
79 #define SIGN(x)     ((x) < 0 ? -1.0 : 1.0)	/**< Sign of the expression */
80 
81 /** Angle normalization between 0 and 2 * PI */
82 #define NORM0_2PI(x) 				\
83 do {						\
84 	while ((x) > 2*PI) { (x) -= 2*PI; }	\
85 	while ((x) < 0) { (x) += 2*PI; } 	\
86 } while (0)
87 
88 /** Angle normalization between -PI and PI */
89 #define NORM_PI_PI(x) 				\
90 do {						\
91 	while ((x) > PI) { (x) -= 2*PI; }	\
92 	while ((x) < -PI) { (x) += 2*PI; } 	\
93 } while (0)
94 
95 
96 #ifndef DIST
97 /** Distance between two points */
98 #define DIST(x1, y1, x2, y2) sqrt(((x1) - (x2)) * ((x1) - (x2)) + ((y1) - (y2)) * ((y1) - (y2)))
99 #endif
100 
101 #ifndef MIN
102 /** Minimum between two values */
103 #define MIN(x,y) ((x) < (y) ? (x) : (y))
104 #endif
105 
106 
107 typedef struct {
108     float	x;
109     float	y;
110     float	z;
111 } t3Df;
112 
113 /** 3D point.
114     @ingroup definitions
115 */
116 typedef struct {
117     tdble	x;		/**< x coordinate */
118     tdble	y;		/**< y coordinate */
119     tdble	z;		/**< z coordinate */
120 } t3Dd;
121 
122 typedef struct {
123     int		x;
124     int		y;
125     int		z;
126 } t3Di;
127 
128 /** 6 DOF position.
129     @ingroup definitions
130 */
131 typedef struct {
132     tdble	x;		/**< x coordinate */
133     tdble	y;		/**< y coordinate */
134     tdble	z;		/**< z coordinate */
135     tdble	ax;		/**< angle along x axis */
136     tdble	ay;		/**< angle along y axis */
137     tdble	az;		/**< angle along z axis */
138 } tPosd;
139 
140 /** Dynamic point structure.
141     @ingroup definitions
142 */
143 typedef struct
144 {
145     tPosd pos; /**< position */
146     tPosd vel; /**< velocity */
147     tPosd acc; /**< acceleration */
148 } tDynPt;
149 
150 /** Forces and moments */
151 typedef struct
152 {
153     t3Dd F; /**< Forces */
154     t3Dd M; /**< Moments */
155 } tForces;
156 
157 
158 // <esppat>
159 #ifdef WIN32
160 #define malloc _tgf_win_malloc
161 #define calloc _tgf_win_calloc
162 #define realloc _tgf_win_realloc
163 #define free _tgf_win_free
164 #define strdup _tgf_win_strdup
165 #define _strdup _tgf_win_strdup
166 extern void * _tgf_win_malloc(size_t size);
167 extern void * _tgf_win_calloc(size_t num, size_t size);
168 extern void * _tgf_win_realloc(void * memblock, size_t size);
169 extern void _tgf_win_free(void * memblock);
170 extern char * _tgf_win_strdup(const char * str);
171 #endif // WIN32
172 // </esppat>
173 
174 /*********************************
175  * Interface For Dynamic Modules *
176  *********************************/
177 
178 /** initialisation of the function table
179     @see	ModInfo
180 */
181 typedef int (*tfModPrivInit)(int index, void *);
182 
183 /** Maximum number of interface in one DLL
184     @see	ModList
185  */
186 #define MAX_MOD_ITF 10
187 
188 /** Module information structure  */
189 typedef struct ModInfo {
190     char		*name;		/**< name of the module (short) (NULL if no module) */
191     char		*desc;		/**< description of the module (can be long) */
192     tfModPrivInit	fctInit;	/**< init function */
193     unsigned int	gfId;		/**< supported framework version */
194     int			index;		/**< index if multiple interface in one dll */
195     int			prio;		/**< priority if needed */
196     int			magic;		/**< magic number for integrity check */
197 } tModInfo;
198 
199 /* module init function interface */
200 typedef int (*tfModInfo)(tModInfo *);	/* first function called in the module */
201 
202 /* module shutdown function interface */
203 typedef int (*tfModShut)(void);	/* last function called in the module */
204 
205 
206 /** list of module interfaces */
207 typedef struct ModList {
208     tModInfo		modInfo[MAX_MOD_ITF];	/**< module info list for this dll */
209 #ifdef _WIN32
210     HMODULE		handle;			/**< handle of loaded module */
211 #else
212     void		*handle;		/**< handle of loaded module */
213 #endif
214     char		*sopath;		/**< path name of file */
215     struct ModList	*next;			/**< next module in list */
216 } tModList;
217 
218 
219 extern int GfModLoad(unsigned int gfid, char *dllname, tModList **modlist);
220 extern int GfModLoadDir(unsigned int gfid, char *dir, tModList **modlist);
221 extern int GfModUnloadList(tModList **modlist);
222 extern int GfModInfo(unsigned int gfid, char *filename, tModList **modlist);
223 extern int GfModInfoDir(unsigned int gfid, char *dir, int level, tModList **modlist);
224 extern int GfModFreeInfoList(tModList **modlist);
225 
226 /************************
227  * Directory management *
228  ************************/
229 
230 /** List of (DLL) files for a Directory
231     @see	GfDirGetList
232 */
233 typedef struct FList
234 {
235 	struct FList *next;		/**< Next entry */
236 	struct FList *prev;		/**< Previous entry */
237 	char *name;				/**< File name */
238 	char *dispName;			/**< Name to display on screen */
239 	void *userData;			/**< User data */
240 } tFList;
241 
242 extern tFList *GfDirGetList(const char *dir);
243 extern tFList *GfDirGetListFiltered(const char *dir, const char *suffix);
244 typedef void (*tfDirfreeUserData)(void*);	/**< Function to call for releasing the user data associated with file entry */
245 extern void GfDirFreeList(tFList *list, tfDirfreeUserData freeUserDatabool, bool freename = false, bool freedispname = false);
246 
247 
248 /**********************************
249  *  Interface For Parameter Files *
250  **********************************/
251 
252 /*
253  *	This set of function is used to store and retrieve
254  *	values in parameters files.
255  */
256 
257 
258 /* parameters file type */
259 #define GFPARM_PARAMETER	0	/**< Parameter file */
260 #define GFPARM_TEMPLATE		1	/**< Template file */
261 #define GFPARM_PARAM_STR	"param"
262 #define GFPARM_TEMPL_STR	"template"
263 
264 /* parameters access mode */
265 #define GFPARM_MODIFIABLE	1	/**< Parameter file allowed to be modified */
266 #define GFPARM_WRITABLE		2	/**< Parameter file allowed to be saved on disk */
267 
268 /* parameter file read */
269 #define GFPARM_RMODE_STD	0x01	/**< if handle already openned return it */
270 #define GFPARM_RMODE_REREAD	0x02	/**< reread the parameters from file and release the previous ones */
271 #define GFPARM_RMODE_CREAT	0x04	/**< Create the file if doesn't exist */
272 #define GFPARM_RMODE_PRIVATE	0x08
273 
274 extern void *GfParmReadFile(const char *file, int mode);
275 /* parameter file write */
276 extern int GfParmWriteFile(const char *file, void* handle, const char *name);
277 extern int GfParmCreateDirectory(const char *file, void *parmHandle);
278 
279 extern char *GfParmGetName(void *handle);
280 extern char *GfParmGetFileName(void *handle);
281 
282 /* set the dtd and header values */
283 extern void GfParmSetDTD (void *parmHandle, char *dtd, char*header);
284 
285 /* get string parameter value */
286 extern const char *GfParmGetStr(void *handle, const char *path, const char *key, const char *deflt);
287 /* get string parameter value */
288 extern const char *GfParmGetCurStr(void *handle, const char *path, const char *key, const char *deflt);
289 /* set string parameter value */
290 extern int GfParmSetStr(void *handle, const char *path, const char *key, const char *val);
291 /* set string parameter value */
292 extern int GfParmSetCurStr(void *handle, const char *path, const char *key, const char *val);
293 
294 /* get num parameter value */
295 extern tdble GfParmGetNum(void *handle, const char *path, const char *key, const char *unit, tdble deflt);
296 /* get num parameter value */
297 extern tdble GfParmGetCurNum(void *handle, const char *path, const char *key, const char *unit, tdble deflt);
298 /* set num parameter value */
299 extern int GfParmSetNum(void *handle, const char *path, const char *key, const char *unit, tdble val);
300 extern int GfParmSetNumEx(void *handle, const char *path, const char *key, const char *unit, tdble val, tdble min, tdble max);
301 /* set num parameter value */
302 extern int GfParmSetCurNum(void *handle, const char *path, const char *key, const char *unit, tdble val);
303 
304 
305 /* clean all the parameters of a set */
306 extern void GfParmClean(void *handle);
307 /* clean the parms and release the handle without updating the file */
308 extern void GfParmReleaseHandle(void *handle);
309 
310 /* Convert a value in "units" into SI */
311 extern tdble GfParmUnit2SI(const char *unit, tdble val);
312 /* convert a value in SI to "units" */
313 extern tdble GfParmSI2Unit(const char *unit, tdble val);
314 
315 /* compare and merge different handles */
316 extern int GfParmCheckHandle(void *ref, void *tgt);
317 #define GFPARM_MMODE_SRC	1 /**< use ref and modify existing parameters with tgt */
318 #define GFPARM_MMODE_DST	2 /**< use tgt and verify ref parameters */
319 #define GFPARM_MMODE_RELSRC	4 /**< release ref after the merge */
320 #define GFPARM_MMODE_RELDST	8 /**< release tgt after the merge */
321 extern void *GfParmMergeHandles(void *ref, void *tgt, int mode);
322 extern int GfParmGetNumBoundaries(void *handle, const char *path, const char *key, tdble *min, tdble *max);
323 
324 
325 extern int GfParmGetEltNb(void *handle, const char *path);
326 extern int GfParmListSeekFirst(void *handle, const char *path);
327 extern int GfParmListSeekNext(void *handle, const char *path);
328 extern char *GfParmListGetCurEltName(void *handle, const char *path);
329 extern int GfParmListClean(void *handle, const char *path);
330 
331 /*******************
332  * Trace Interface *
333  *******************/
334 
335 #ifdef WIN32
336 #define GfTrace	printf
337 #define GfFatal printf
338 #else
339 
340 #define GfTrace printf
341 
342 static inline void
GfFatal(const char * fmt,...)343 GfFatal(const char *fmt, ...)
344 {
345     va_list ap;
346     va_start(ap, fmt);
347     vprintf(fmt, ap);
348     va_end(ap);
349     /* GfScrShutdown(); */
350     assert (0);
351     exit (1);
352 }
353 #endif
354 
355 #define GfError printf
356 
357 #if !(_DEBUG || DEBUG)
358 //#ifdef WIN32
359 //#define GfOut printf
360 //#else
361 
362 
363 /** Console output
364     @param	s	string to display
365     @param	args	printf args
366     @fn	 GfOut(s, args...)
367  */
368 static inline void
GfOut(const char * fmt,...)369 GfOut(const char *fmt, ...)
370 {
371 }
372 
373 //#endif /* WIN32 */
374 
375 #else /* _DEBUG || DEBUG */
376 
377 #define GfOut printf
378 
379 #endif /* _DEBUG || DEBUG */
380 
381 /*******************
382  * Time  Interface *
383  *******************/
384 extern double GfTimeClock(void);
385 
386 /* Mean values */
387 #define GF_MEAN_MAX_VAL	5
388 
389 typedef struct
390 {
391     int		curNum;
392     tdble	val[GF_MEAN_MAX_VAL+1];
393 } tMeanVal;
394 
395 extern tdble gfMean(tdble v, tMeanVal *pvt, int n, int w);
396 extern void gfMeanReset(tdble v, tMeanVal *pvt);
397 
398 /* MISC */
399 extern char *GetLocalDir(void);
400 extern void SetLocalDir(char *buf);
401 extern char *GetLibDir(void);
402 extern void SetLibDir(char *buf);
403 extern char *GetDataDir(void);
404 extern void SetDataDir(char *buf);
405 extern int GetSingleTextureMode (void);
406 extern void SetSingleTextureMode (void);
407 extern int GfNearestPow2 (int x);
408 extern int GfCreateDir(char *path);
409 extern int GfCreateDirForFile(const char *filenameandpath);
410 
411 
412 /*
413  * Copyright (c) 1991, 1993
414  *	The Regents of the University of California.  All rights reserved.
415  *
416  * Redistribution and use in source and binary forms, with or without
417  * modification, are permitted provided that the following conditions
418  * are met:
419  * 1. Redistributions of source code must retain the above copyright
420  *    notice, this list of conditions and the following disclaimer.
421  * 2. Redistributions in binary form must reproduce the above copyright
422  *    notice, this list of conditions and the following disclaimer in the
423  *    documentation and/or other materials provided with the distribution.
424  * 3. All advertising materials mentioning features or use of this software
425  *    must display the following acknowledgement:
426  *	This product includes software developed by the University of
427  *	California, Berkeley and its contributors.
428  * 4. Neither the name of the University nor the names of its contributors
429  *    may be used to endorse or promote products derived from this software
430  *    without specific prior written permission.
431  *
432  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
433  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
434  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
435  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
436  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
437  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
438  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
439  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
440  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
441  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
442  * SUCH DAMAGE.
443  *
444  *	@(#)queue.h	8.5 (Berkeley) 8/20/94
445  */
446 
447 /*
448  * Tail queue definitions.
449  */
450 /** Head type definition
451     @ingroup tailq */
452 #define GF_TAILQ_HEAD(name, type)					\
453 typedef struct name {							\
454 	type *tqh_first;	/* first element */			\
455 	type **tqh_last;	/* addr of last next element */		\
456 } t ## name
457 
458 /** Entry in structure
459     @ingroup tailq */
460 #define GF_TAILQ_ENTRY(type)						\
461 struct {								\
462 	type *tqe_next;	/* next element */				\
463 	type **tqe_prev;	/* address of previous next element */	\
464 }
465 
466 /** First element of a TAILQ
467     @ingroup tailq */
468 #define	GF_TAILQ_FIRST(head)		((head)->tqh_first)
469 /** Next element of a TAILQ
470     @ingroup tailq */
471 #define	GF_TAILQ_NEXT(elm, field)	((elm)->field.tqe_next)
472 /** End of a TAILQ
473     @ingroup tailq */
474 #define	GF_TAILQ_END(head)		NULL
475 /** Last element of a TAILQ
476     @ingroup tailq */
477 #define GF_TAILQ_LAST(head, headname) 					\
478 	(*(((struct headname *)((head)->tqh_last))->tqh_last))
479 /** Previous element of a TAILQ
480     @ingroup tailq */
481 #define GF_TAILQ_PREV(elm, headname, field) 				\
482 	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
483 
484 /*
485  * Tail queue functions.
486  */
487 /** Head initialization (Mandatory)
488     @ingroup tailq */
489 #define	GF_TAILQ_INIT(head) do {					\
490 	(head)->tqh_first = NULL;					\
491 	(head)->tqh_last = &(head)->tqh_first;				\
492 } while (0)
493 
494 /** Entry initialization (optionnal if inserted)
495     @ingroup tailq */
496 #define GF_TAILQ_INIT_ENTRY(elm, field) do {	\
497   (elm)->field.tqe_next = 0;			\
498   (elm)->field.tqe_prev = 0;			\
499 } while (0)
500 
501 /** Insert an element at the head
502     @ingroup tailq */
503 #define GF_TAILQ_INSERT_HEAD(head, elm, field) do {			\
504 	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
505 		(head)->tqh_first->field.tqe_prev =			\
506 		    &(elm)->field.tqe_next;				\
507 	else								\
508 		(head)->tqh_last = &(elm)->field.tqe_next;		\
509 	(head)->tqh_first = (elm);					\
510 	(elm)->field.tqe_prev = &(head)->tqh_first;			\
511 } while (0)
512 
513 /** Insert an element at the tail
514     @ingroup tailq */
515 #define GF_TAILQ_INSERT_TAIL(head, elm, field) do {			\
516 	(elm)->field.tqe_next = NULL;					\
517 	(elm)->field.tqe_prev = (head)->tqh_last;			\
518 	*(head)->tqh_last = (elm);					\
519 	(head)->tqh_last = &(elm)->field.tqe_next;			\
520 } while (0)
521 
522 /** Insert an element after another element
523     @ingroup tailq */
524 #define GF_TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
525 	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
526 		(elm)->field.tqe_next->field.tqe_prev = 		\
527 		    &(elm)->field.tqe_next;				\
528 	else								\
529 		(head)->tqh_last = &(elm)->field.tqe_next;		\
530 	(listelm)->field.tqe_next = (elm);				\
531 	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
532 } while (0)
533 
534 /** Insert an element before another element
535     @ingroup tailq */
536 #define	GF_TAILQ_INSERT_BEFORE(listelm, elm, field) do {		\
537 	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
538 	(elm)->field.tqe_next = (listelm);				\
539 	*(listelm)->field.tqe_prev = (elm);				\
540 	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
541 } while (0)
542 
543 /** Remove an element
544     @ingroup tailq */
545 #define GF_TAILQ_REMOVE(head, elm, field) do {				\
546 	if (((elm)->field.tqe_next) != NULL)				\
547 		(elm)->field.tqe_next->field.tqe_prev = 		\
548 		    (elm)->field.tqe_prev;				\
549 	else								\
550 		(head)->tqh_last = (elm)->field.tqe_prev;		\
551 	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
552 } while (0)
553 
554 
555 /* author      : Henrik Enqvist IB (henqvist@abo.fi) */
556 #ifdef PROFILER
557 
558 #include <vector>
559 #include <map>
560 
561 #define START_PROFILE(name) Profiler::getInstance()->startProfile(name)
562 #define STOP_PROFILE(name) Profiler::getInstance()->stopProfile()
563 #define STOP_ACTIVE_PROFILES() Profiler::getInstance()->stopActiveProfiles()
564 #define PRINT_PROFILE() Profiler::getInstance()->printProfile()
565 
566 class ProfileInstance {
567  public:
568   ProfileInstance(const char * pname);
569   ~ProfileInstance();
570   char name[256];
571   int calls;
572   int openCalls;
573   double totalTime;
574   double addTime;
575   double subTime;
576   double tmpStart;
577   std::map<ProfileInstance *, void *> mapChildren;
578 };
579 
580 /** A simple high-level profiler for non-threaded non-recursive functions. */
581 class Profiler {
582  protected:
583   Profiler();
584  public:
585   ~Profiler();
586   static Profiler * getInstance();
587   void startProfile(const char * pname);
588   void stopProfile();
589   void stopActiveProfiles();
590   void printProfile();
591  private:
592   static Profiler * profiler;
593   ProfileInstance * curProfile;
594   double fStartTime;
595   std::vector<ProfileInstance *> vecProfiles;
596   std::vector<ProfileInstance *> stkProfiles;
597   std::map<ProfileInstance *, void *> mapWarning;
598 };
599 
600 #else /* PROFILER */
601 #define START_PROFILE(a)
602 #define STOP_PROFILE(a)
603 #define STOP_ACTIVE_PROFILES()
604 #define PRINT_PROFILE()
605 #endif
606 
607 /*******************/
608 /*   Hash Tables   */
609 /*******************/
610 #define GF_HASH_TYPE_STR	0	/**< String key based hash table */
611 #define GF_HASH_TYPE_BUF	1	/**< Memory buffer key based hash table */
612 
613 typedef void (*tfHashFree)(const void*);	/**< Function to call for releasing the user data associated with hash table */
614 
615 void *GfHashCreate(int type);
616 int GfHashAddStr(void *hash, const char *key, const void *data);
617 const void *GfHashRemStr(void *hash, char *key);
618 const void *GfHashGetStr(void *hash, const char *key);
619 void GfHashAddBuf(void *hash, char *key, size_t sz, void *data);
620 const void *GfHashRemBuf(void *hash, char *key, size_t sz);
621 const void *GfHashGetBuf(void *hash, char *key, size_t sz);
622 void GfHashRelease(void *hash, tfHashFree hashFree);
623 const void *GfHashGetFirst(void *hash);
624 const void *GfHashGetNext(void *hash);
625 
626 #define GF_DIR_CREATION_FAILED 0
627 #define GF_DIR_CREATED 1
628 
629 #endif /* __TGF__H__ */
630 
631 
632