]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/main.c
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / tools / quake3 / q3map2 / main.c
1 /* -------------------------------------------------------------------------------
2
3    Copyright (C) 1999-2007 id Software, Inc. and contributors.
4    For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6    This file is part of GtkRadiant.
7
8    GtkRadiant is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    GtkRadiant is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with GtkRadiant; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22    -------------------------------------------------------------------------------
23
24    This code has been altered significantly from its original form, to support
25    several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27    ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define MAIN_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39 /*
40    Random()
41    returns a pseudorandom number between 0 and 1
42  */
43
44 vec_t Random( void ){
45         return (vec_t) rand() / RAND_MAX;
46 }
47
48
49
50 /*
51    ExitQ3Map()
52    cleanup routine
53  */
54
55 static void ExitQ3Map( void ){
56         BSPFilesCleanup();
57         if ( mapDrawSurfs != NULL ) {
58                 free( mapDrawSurfs );
59         }
60 }
61
62 static int MD4BlockChecksum( void * buffer, int length ) {
63         unsigned char digest[16];
64         int checksum;
65
66         md4_get_digest( buffer, length, digest );
67         /* I suppose it has to be done that way for legacy reasons? */
68         checksum = digest[0] & ( digest[1] << 8 ) & ( digest[2] << 16 ) & ( digest[3] << 24 );
69         checksum ^= digest[4] & ( digest[5] << 8 ) & ( digest[6] << 16 ) & ( digest[7] << 24 );
70         checksum ^= digest[8] & ( digest[9] << 8 ) & ( digest[10] << 16 ) & ( digest[11] << 24 );
71         checksum ^= digest[12] & ( digest[13] << 8 ) & ( digest[14] << 16 ) & ( digest[15] << 24 );
72         return checksum;
73 }
74
75 /*
76    FixAAS()
77    resets an aas checksum to match the given BSP
78  */
79
80 int FixAAS( int argc, char **argv ){
81         int length, checksum;
82         void        *buffer;
83         FILE        *file;
84         char aas[ 1024 ], **ext;
85         char        *exts[] =
86         {
87                 ".aas",
88                 "_b0.aas",
89                 "_b1.aas",
90                 NULL
91         };
92
93
94         /* arg checking */
95         if ( argc < 2 ) {
96                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
97                 return 0;
98         }
99
100         /* do some path mangling */
101         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
102         StripExtension( source );
103         DefaultExtension( source, ".bsp" );
104
105         /* note it */
106         Sys_Printf( "--- FixAAS ---\n" );
107
108         /* load the bsp */
109         Sys_Printf( "Loading %s\n", source );
110         length = LoadFile( source, &buffer );
111
112         /* create bsp checksum */
113         Sys_Printf( "Creating checksum...\n" );
114         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
115
116         /* write checksum to aas */
117         ext = exts;
118         while ( *ext )
119         {
120                 /* mangle name */
121                 strcpy( aas, source );
122                 StripExtension( aas );
123                 strcat( aas, *ext );
124                 Sys_Printf( "Trying %s\n", aas );
125                 ext++;
126
127                 /* fix it */
128                 file = fopen( aas, "r+b" );
129                 if ( !file ) {
130                         continue;
131                 }
132                 if ( fwrite( &checksum, 4, 1, file ) != 1 ) {
133                         Error( "Error writing checksum to %s", aas );
134                 }
135                 fclose( file );
136         }
137
138         /* return to sender */
139         return 0;
140 }
141
142
143
144 /*
145    AnalyzeBSP() - ydnar
146    analyzes a Quake engine BSP file
147  */
148
149 typedef struct abspHeader_s
150 {
151         char ident[ 4 ];
152         int version;
153
154         bspLump_t lumps[ 1 ];       /* unknown size */
155 }
156 abspHeader_t;
157
158 typedef struct abspLumpTest_s
159 {
160         int radix, minCount;
161         char            *name;
162 }
163 abspLumpTest_t;
164
165 int AnalyzeBSP( int argc, char **argv ){
166         abspHeader_t            *header;
167         int size, i, version, offset, length, lumpInt, count;
168         char ident[ 5 ];
169         void                    *lump;
170         float lumpFloat;
171         char lumpString[ 1024 ], source[ 1024 ];
172         qboolean lumpSwap = qfalse;
173         abspLumpTest_t          *lumpTest;
174         static abspLumpTest_t lumpTests[] =
175         {
176                 { sizeof( bspPlane_t ),         6,      "IBSP LUMP_PLANES" },
177                 { sizeof( bspBrush_t ),         1,      "IBSP LUMP_BRUSHES" },
178                 { 8,                            6,      "IBSP LUMP_BRUSHSIDES" },
179                 { sizeof( bspBrushSide_t ),     6,      "RBSP LUMP_BRUSHSIDES" },
180                 { sizeof( bspModel_t ),         1,      "IBSP LUMP_MODELS" },
181                 { sizeof( bspNode_t ),          2,      "IBSP LUMP_NODES" },
182                 { sizeof( bspLeaf_t ),          1,      "IBSP LUMP_LEAFS" },
183                 { 104,                          3,      "IBSP LUMP_DRAWSURFS" },
184                 { 44,                           3,      "IBSP LUMP_DRAWVERTS" },
185                 { 4,                            6,      "IBSP LUMP_DRAWINDEXES" },
186                 { 128 * 128 * 3,                1,      "IBSP LUMP_LIGHTMAPS" },
187                 { 256 * 256 * 3,                1,      "IBSP LUMP_LIGHTMAPS (256 x 256)" },
188                 { 512 * 512 * 3,                1,      "IBSP LUMP_LIGHTMAPS (512 x 512)" },
189                 { 0, 0, NULL }
190         };
191
192
193         /* arg checking */
194         if ( argc < 1 ) {
195                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
196                 return 0;
197         }
198
199         /* process arguments */
200         for ( i = 1; i < ( argc - 1 ); i++ )
201         {
202                 /* -format map|ase|... */
203                 if ( !strcmp( argv[ i ],  "-lumpswap" ) ) {
204                         Sys_Printf( "Swapped lump structs enabled\n" );
205                         lumpSwap = qtrue;
206                 }
207         }
208
209         /* clean up map name */
210         strcpy( source, ExpandArg( argv[ i ] ) );
211         Sys_Printf( "Loading %s\n", source );
212
213         /* load the file */
214         size = LoadFile( source, (void**) &header );
215         if ( size == 0 || header == NULL ) {
216                 Sys_Printf( "Unable to load %s.\n", source );
217                 return -1;
218         }
219
220         /* analyze ident/version */
221         memcpy( ident, header->ident, 4 );
222         ident[ 4 ] = '\0';
223         version = LittleLong( header->version );
224
225         Sys_Printf( "Identity:      %s\n", ident );
226         Sys_Printf( "Version:       %d\n", version );
227         Sys_Printf( "---------------------------------------\n" );
228
229         /* analyze each lump */
230         for ( i = 0; i < 100; i++ )
231         {
232                 /* call of duty swapped lump pairs */
233                 if ( lumpSwap ) {
234                         offset = LittleLong( header->lumps[ i ].length );
235                         length = LittleLong( header->lumps[ i ].offset );
236                 }
237
238                 /* standard lump pairs */
239                 else
240                 {
241                         offset = LittleLong( header->lumps[ i ].offset );
242                         length = LittleLong( header->lumps[ i ].length );
243                 }
244
245                 /* extract data */
246                 lump = (byte*) header + offset;
247                 lumpInt = LittleLong( (int) *( (int*) lump ) );
248                 lumpFloat = LittleFloat( (float) *( (float*) lump ) );
249                 memcpy( lumpString, (char*) lump, ( length < 1024 ? length : 1024 ) );
250                 lumpString[ 1024 ] = '\0';
251
252                 /* print basic lump info */
253                 Sys_Printf( "Lump:          %d\n", i );
254                 Sys_Printf( "Offset:        %d bytes\n", offset );
255                 Sys_Printf( "Length:        %d bytes\n", length );
256
257                 /* only operate on valid lumps */
258                 if ( length > 0 ) {
259                         /* print data in 4 formats */
260                         Sys_Printf( "As hex:        %08X\n", lumpInt );
261                         Sys_Printf( "As int:        %d\n", lumpInt );
262                         Sys_Printf( "As float:      %f\n", lumpFloat );
263                         Sys_Printf( "As string:     %s\n", lumpString );
264
265                         /* guess lump type */
266                         if ( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' ) {
267                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
268                         }
269                         else if ( strstr( lumpString, "textures/" ) ) {
270                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
271                         }
272                         else
273                         {
274                                 /* guess based on size/count */
275                                 for ( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
276                                 {
277                                         if ( ( length % lumpTest->radix ) != 0 ) {
278                                                 continue;
279                                         }
280                                         count = length / lumpTest->radix;
281                                         if ( count < lumpTest->minCount ) {
282                                                 continue;
283                                         }
284                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
285                                 }
286                         }
287                 }
288
289                 Sys_Printf( "---------------------------------------\n" );
290
291                 /* end of file */
292                 if ( offset + length >= size ) {
293                         break;
294                 }
295         }
296
297         /* last stats */
298         Sys_Printf( "Lump count:    %d\n", i + 1 );
299         Sys_Printf( "File size:     %d bytes\n", size );
300
301         /* return to caller */
302         return 0;
303 }
304
305
306
307 /*
308    BSPInfo()
309    emits statistics about the bsp file
310  */
311
312 int BSPInfo( int count, char **fileNames ){
313         int i;
314         char source[ 1024 ], ext[ 64 ];
315         int size;
316         FILE        *f;
317
318
319         /* dummy check */
320         if ( count < 1 ) {
321                 Sys_Printf( "No files to dump info for.\n" );
322                 return -1;
323         }
324
325         /* enable info mode */
326         infoMode = qtrue;
327
328         /* walk file list */
329         for ( i = 0; i < count; i++ )
330         {
331                 Sys_Printf( "---------------------------------\n" );
332
333                 /* mangle filename and get size */
334                 strcpy( source, fileNames[ i ] );
335                 ExtractFileExtension( source, ext );
336                 if ( !Q_stricmp( ext, "map" ) ) {
337                         StripExtension( source );
338                 }
339                 DefaultExtension( source, ".bsp" );
340                 f = fopen( source, "rb" );
341                 if ( f ) {
342                         size = Q_filelength( f );
343                         fclose( f );
344                 }
345                 else{
346                         size = 0;
347                 }
348
349                 /* load the bsp file and print lump sizes */
350                 Sys_Printf( "%s\n", source );
351                 LoadBSPFile( source );
352                 PrintBSPFileSizes();
353
354                 /* print sizes */
355                 Sys_Printf( "\n" );
356                 Sys_Printf( "          total         %9d\n", size );
357                 Sys_Printf( "                        %9d KB\n", size / 1024 );
358                 Sys_Printf( "                        %9d MB\n", size / ( 1024 * 1024 ) );
359
360                 Sys_Printf( "---------------------------------\n" );
361         }
362
363         /* return count */
364         return i;
365 }
366
367
368
369 /*
370    ScaleBSPMain()
371    amaze and confuse your enemies with wierd scaled maps!
372  */
373
374 int ScaleBSPMain( int argc, char **argv ){
375         int i;
376         float f, scale;
377         vec3_t vec;
378         char str[ 1024 ];
379
380
381         /* arg checking */
382         if ( argc < 2 ) {
383                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
384                 return 0;
385         }
386
387         /* get scale */
388         scale = atof( argv[ argc - 2 ] );
389         if ( scale == 0.0f ) {
390                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
391                 Sys_Printf( "Non-zero scale value required.\n" );
392                 return 0;
393         }
394
395         /* do some path mangling */
396         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
397         StripExtension( source );
398         DefaultExtension( source, ".bsp" );
399
400         /* load the bsp */
401         Sys_Printf( "Loading %s\n", source );
402         LoadBSPFile( source );
403         ParseEntities();
404
405         /* note it */
406         Sys_Printf( "--- ScaleBSP ---\n" );
407         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
408
409         /* scale entity keys */
410         for ( i = 0; i < numBSPEntities && i < numEntities; i++ )
411         {
412                 /* scale origin */
413                 GetVectorForKey( &entities[ i ], "origin", vec );
414                 if ( ( vec[ 0 ] + vec[ 1 ] + vec[ 2 ] ) ) {
415                         VectorScale( vec, scale, vec );
416                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
417                         SetKeyValue( &entities[ i ], "origin", str );
418                 }
419
420                 /* scale door lip */
421                 f = FloatForKey( &entities[ i ], "lip" );
422                 if ( f ) {
423                         f *= scale;
424                         sprintf( str, "%f", f );
425                         SetKeyValue( &entities[ i ], "lip", str );
426                 }
427         }
428
429         /* scale models */
430         for ( i = 0; i < numBSPModels; i++ )
431         {
432                 VectorScale( bspModels[ i ].mins, scale, bspModels[ i ].mins );
433                 VectorScale( bspModels[ i ].maxs, scale, bspModels[ i ].maxs );
434         }
435
436         /* scale nodes */
437         for ( i = 0; i < numBSPNodes; i++ )
438         {
439                 VectorScale( bspNodes[ i ].mins, scale, bspNodes[ i ].mins );
440                 VectorScale( bspNodes[ i ].maxs, scale, bspNodes[ i ].maxs );
441         }
442
443         /* scale leafs */
444         for ( i = 0; i < numBSPLeafs; i++ )
445         {
446                 VectorScale( bspLeafs[ i ].mins, scale, bspLeafs[ i ].mins );
447                 VectorScale( bspLeafs[ i ].maxs, scale, bspLeafs[ i ].maxs );
448         }
449
450         /* scale drawverts */
451         for ( i = 0; i < numBSPDrawVerts; i++ )
452                 VectorScale( bspDrawVerts[ i ].xyz, scale, bspDrawVerts[ i ].xyz );
453
454         /* scale planes */
455         for ( i = 0; i < numBSPPlanes; i++ )
456                 bspPlanes[ i ].dist *= scale;
457
458         /* scale gridsize */
459         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
460         if ( ( vec[ 0 ] + vec[ 1 ] + vec[ 2 ] ) == 0.0f ) {
461                 VectorCopy( gridSize, vec );
462         }
463         VectorScale( vec, scale, vec );
464         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
465         SetKeyValue( &entities[ 0 ], "gridsize", str );
466
467         /* write the bsp */
468         UnparseEntities();
469         StripExtension( source );
470         DefaultExtension( source, "_s.bsp" );
471         Sys_Printf( "Writing %s\n", source );
472         WriteBSPFile( source );
473
474         /* return to sender */
475         return 0;
476 }
477
478
479
480 /*
481    ConvertBSPMain()
482    main argument processing function for bsp conversion
483  */
484
485 int ConvertBSPMain( int argc, char **argv ){
486         int i;
487         int ( *convertFunc )( char * );
488         game_t  *convertGame;
489
490
491         /* set default */
492         convertFunc = ConvertBSPToASE;
493         convertGame = NULL;
494
495         /* arg checking */
496         if ( argc < 1 ) {
497                 Sys_Printf( "Usage: q3map -convert [-format <ase|map>] [-v] <mapname>\n" );
498                 return 0;
499         }
500
501         /* process arguments */
502         for ( i = 1; i < ( argc - 1 ); i++ )
503         {
504                 /* -format map|ase|... */
505                 if ( !strcmp( argv[ i ],  "-format" ) ) {
506                         i++;
507                         if ( !Q_stricmp( argv[ i ], "ase" ) ) {
508                                 convertFunc = ConvertBSPToASE;
509                         }
510                         else if ( !Q_stricmp( argv[ i ], "map" ) ) {
511                                 convertFunc = ConvertBSPToMap;
512                         }
513                         else
514                         {
515                                 convertGame = GetGame( argv[ i ] );
516                                 if ( convertGame == NULL ) {
517                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
518                                 }
519                         }
520                 }
521         }
522
523         /* clean up map name */
524         strcpy( source, ExpandArg( argv[ i ] ) );
525         StripExtension( source );
526         DefaultExtension( source, ".bsp" );
527
528         LoadShaderInfo();
529
530         Sys_Printf( "Loading %s\n", source );
531
532         /* ydnar: load surface file */
533         //%     LoadSurfaceExtraFile( source );
534
535         LoadBSPFile( source );
536
537         /* parse bsp entities */
538         ParseEntities();
539
540         /* bsp format convert? */
541         if ( convertGame != NULL ) {
542                 /* set global game */
543                 game = convertGame;
544
545                 /* write bsp */
546                 StripExtension( source );
547                 DefaultExtension( source, "_c.bsp" );
548                 Sys_Printf( "Writing %s\n", source );
549                 WriteBSPFile( source );
550
551                 /* return to sender */
552                 return 0;
553         }
554
555         /* normal convert */
556         return convertFunc( source );
557 }
558
559
560
561 /*
562    main()
563    q3map mojo...
564  */
565
566 int main( int argc, char **argv ){
567         int i, r;
568         double start, end;
569
570
571         /* we want consistent 'randomness' */
572         srand( 0 );
573
574         /* start timer */
575         start = I_FloatTime();
576
577         /* this was changed to emit version number over the network */
578         printf( Q3MAP_VERSION "\n" );
579
580         /* set exit call */
581         atexit( ExitQ3Map );
582
583         /* read general options first */
584         for ( i = 1; i < argc; i++ )
585         {
586                 /* -connect */
587                 if ( !strcmp( argv[ i ], "-connect" ) ) {
588                         argv[ i ] = NULL;
589                         i++;
590                         Broadcast_Setup( argv[ i ] );
591                         argv[ i ] = NULL;
592                 }
593
594                 /* verbose */
595                 else if ( !strcmp( argv[ i ], "-v" ) ) {
596                         verbose = qtrue;
597                         argv[ i ] = NULL;
598                 }
599
600                 /* force */
601                 else if ( !strcmp( argv[ i ], "-force" ) ) {
602                         force = qtrue;
603                         argv[ i ] = NULL;
604                 }
605
606                 /* patch subdivisions */
607                 else if ( !strcmp( argv[ i ], "-subdivisions" ) ) {
608                         argv[ i ] = NULL;
609                         i++;
610                         patchSubdivisions = atoi( argv[ i ] );
611                         argv[ i ] = NULL;
612                         if ( patchSubdivisions <= 0 ) {
613                                 patchSubdivisions = 1;
614                         }
615                 }
616
617                 /* threads */
618                 else if ( !strcmp( argv[ i ], "-threads" ) ) {
619                         argv[ i ] = NULL;
620                         i++;
621                         numthreads = atoi( argv[ i ] );
622                         argv[ i ] = NULL;
623                 }
624         }
625
626         /* init model library */
627         PicoInit();
628         PicoSetMallocFunc( safe_malloc );
629         PicoSetFreeFunc( free );
630         PicoSetPrintFunc( PicoPrintFunc );
631         PicoSetLoadFileFunc( PicoLoadFileFunc );
632         PicoSetFreeFileFunc( free );
633
634         /* set number of threads */
635         ThreadSetDefault();
636
637         /* generate sinusoid jitter table */
638         for ( i = 0; i < MAX_JITTERS; i++ )
639         {
640                 jitters[ i ] = sin( i * 139.54152147 );
641                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
642         }
643
644         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
645            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
646
647         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
648         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
649         Sys_Printf( "GtkRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
650         Sys_Printf( "%s\n", Q3MAP_MOTD );
651
652         /* ydnar: new path initialization */
653         InitPaths( &argc, argv );
654
655         /* check if we have enough options left to attempt something */
656         if ( argc < 2 ) {
657                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
658         }
659
660         /* fixaas */
661         if ( !strcmp( argv[ 1 ], "-fixaas" ) ) {
662                 r = FixAAS( argc - 1, argv + 1 );
663         }
664
665         /* analyze */
666         else if ( !strcmp( argv[ 1 ], "-analyze" ) ) {
667                 r = AnalyzeBSP( argc - 1, argv + 1 );
668         }
669
670         /* info */
671         else if ( !strcmp( argv[ 1 ], "-info" ) ) {
672                 r = BSPInfo( argc - 2, argv + 2 );
673         }
674
675         /* vis */
676         else if ( !strcmp( argv[ 1 ], "-vis" ) ) {
677                 r = VisMain( argc - 1, argv + 1 );
678         }
679
680         /* light */
681         else if ( !strcmp( argv[ 1 ], "-light" ) ) {
682                 r = LightMain( argc - 1, argv + 1 );
683         }
684
685         /* vlight */
686         else if ( !strcmp( argv[ 1 ], "-vlight" ) ) {
687                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
688                 argv[ 1 ] = "-fast";    /* eek a hack */
689                 r = LightMain( argc, argv );
690         }
691
692         /* ydnar: lightmap export */
693         else if ( !strcmp( argv[ 1 ], "-export" ) ) {
694                 r = ExportLightmapsMain( argc - 1, argv + 1 );
695         }
696
697         /* ydnar: lightmap import */
698         else if ( !strcmp( argv[ 1 ], "-import" ) ) {
699                 r = ImportLightmapsMain( argc - 1, argv + 1 );
700         }
701
702         /* ydnar: bsp scaling */
703         else if ( !strcmp( argv[ 1 ], "-scale" ) ) {
704                 r = ScaleBSPMain( argc - 1, argv + 1 );
705         }
706
707         /* ydnar: bsp conversion */
708         else if ( !strcmp( argv[ 1 ], "-convert" ) ) {
709                 r = ConvertBSPMain( argc - 1, argv + 1 );
710         }
711
712         /* ydnar: otherwise create a bsp */
713         else{
714                 r = BSPMain( argc, argv );
715         }
716
717         /* emit time */
718         end = I_FloatTime();
719         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
720
721         /* shut down connection */
722         Broadcast_Shutdown();
723
724         /* return any error code */
725         return r;
726 }