demo-reader 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,3 +19,7 @@ rdoc
19
19
  pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
+ *.o
23
+ ext/dm68/Makefile
24
+ ext/dm68/dm68.bundle
25
+
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = demo-reader
2
2
 
3
- A library to parse warsow demo files
3
+ A library to parse warsow and q3 demo files
4
4
 
5
5
  == Note on Patches/Pull Requests
6
6
 
data/Rakefile CHANGED
@@ -5,8 +5,8 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "demo-reader"
8
- gem.summary = %Q{A library to read warsow demo files}
9
- gem.description = %Q{A library to read warsow demo files (.wd8, .wd9, .wd10, .wd11 files)}
8
+ gem.summary = %Q{A library to read warsow and q3 demo files}
9
+ gem.description = %Q{A library to read warsow demo files (.wd8, .wd9, .wd10, .wd11 files) and q3 demo files (*.dm68)}
10
10
  gem.email = "me@aekym.com"
11
11
  gem.homepage = "http://github.com/aekym/demo-reader"
12
12
  gem.authors = ["aekym"]
@@ -49,3 +49,4 @@ Rake::RDocTask.new do |rdoc|
49
49
  rdoc.rdoc_files.include('README*')
50
50
  rdoc.rdoc_files.include('lib/**/*.rb')
51
51
  end
52
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.0
data/demo-reader.gemspec CHANGED
@@ -5,13 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{demo-reader}
8
- s.version = "0.0.2"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["aekym"]
12
- s.date = %q{2010-01-23}
13
- s.description = %q{A library to read warsow demo files (.wd8, .wd9, .wd10, .wd11 files)}
12
+ s.date = %q{2010-08-03}
13
+ s.description = %q{A library to read warsow demo files (.wd8, .wd9, .wd10, .wd11 files) and q3 demo files (*.dm68)}
14
14
  s.email = %q{me@aekym.com}
15
+ s.extensions = ["ext/dm68/extconf.rb"]
15
16
  s.extra_rdoc_files = [
16
17
  "LICENSE",
17
18
  "README.rdoc"
@@ -23,11 +24,31 @@ Gem::Specification.new do |s|
23
24
  "Rakefile",
24
25
  "VERSION",
25
26
  "demo-reader.gemspec",
27
+ "ext/dm68/README.txt",
28
+ "ext/dm68/common.c",
29
+ "ext/dm68/common.h",
30
+ "ext/dm68/dm68.c",
31
+ "ext/dm68/dump.c",
32
+ "ext/dm68/extconf.rb",
33
+ "ext/dm68/huff.c",
34
+ "ext/dm68/huff.h",
35
+ "ext/dm68/main.c",
36
+ "ext/dm68/main.h",
37
+ "ext/dm68/msg.c",
38
+ "ext/dm68/msg.h",
39
+ "ext/dm68/parse.c",
26
40
  "lib/demo-reader.rb",
41
+ "lib/demo_reader_defrag.rb",
27
42
  "lib/demo_reader_warsow.rb",
43
+ "test/demo_reader_defrag_dm_68_cpm_test.rb",
44
+ "test/demo_reader_defrag_dm_68_vq3_test.rb",
28
45
  "test/demo_reader_test.rb",
29
46
  "test/demo_reader_warsow_wd10_race_test.rb",
30
47
  "test/demo_reader_warsow_wd11_race_test.rb",
48
+ "test/fixtures/defrag/dm_68/cpm/pornchronostar_mdf.cpm_00.49.216_tyaz.germany.dm_68",
49
+ "test/fixtures/defrag/dm_68/cpm/puremotion_df.cpm_00.10.600_eS-Rody.russia.dm_68",
50
+ "test/fixtures/defrag/dm_68/vq3/runkull2_df.vq3_01.05.904_XunderBIRD.Germany.dm_68",
51
+ "test/fixtures/defrag/dm_68/vq3/un-dead029_df.vq3_00.16.912_uN-DeaD!WiNTeR.ru.dm_68",
31
52
  "test/fixtures/warsow/wd10/racesow_0.42.b2/dinirun2_racesow_0.42.b2.wd10",
32
53
  "test/fixtures/warsow/wd10/racesow_local/boris.wd10",
33
54
  "test/fixtures/warsow/wd10/racesow_local/die11.7.wd10",
@@ -53,10 +74,12 @@ Gem::Specification.new do |s|
53
74
  s.homepage = %q{http://github.com/aekym/demo-reader}
54
75
  s.rdoc_options = ["--charset=UTF-8"]
55
76
  s.require_paths = ["lib"]
56
- s.rubygems_version = %q{1.3.5}
57
- s.summary = %q{A library to read warsow demo files}
77
+ s.rubygems_version = %q{1.3.7}
78
+ s.summary = %q{A library to read warsow and q3 demo files}
58
79
  s.test_files = [
59
- "test/demo_reader_test.rb",
80
+ "test/demo_reader_defrag_dm_68_cpm_test.rb",
81
+ "test/demo_reader_defrag_dm_68_vq3_test.rb",
82
+ "test/demo_reader_test.rb",
60
83
  "test/demo_reader_warsow_wd10_race_test.rb",
61
84
  "test/demo_reader_warsow_wd11_race_test.rb",
62
85
  "test/helper.rb"
@@ -66,7 +89,7 @@ Gem::Specification.new do |s|
66
89
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
67
90
  s.specification_version = 3
68
91
 
69
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
92
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
70
93
  else
71
94
  end
72
95
  else
@@ -0,0 +1,14 @@
1
+ =================================================
2
+ Quake III *.dm_6? Demo Specifications
3
+
4
+ Copyright (C) 2003 Andrey '[SkulleR]' Nazarov
5
+ Based on Argus and Quake II source code
6
+ Also contains some stuff from Q3A SDK
7
+
8
+ Argus is Copyright (C) 2000 Martin Otten
9
+ Quake II and Quake III are Copyright (C) 1997-2001 ID Software, Inc
10
+ =================================================
11
+
12
+ dm_68 is an example application that parses *.dm_66, *.dm_67 or *.dm_68 demo files and dumps infostrings, server commands and obituaries. Source code included (under GPL).
13
+
14
+ Enjoy!
data/ext/dm68/common.c ADDED
@@ -0,0 +1,797 @@
1
+ // Copyright (C) 1999-2000 Id Software, Inc.
2
+ //
3
+ // q_shared.c -- stateless support routines that are included in each code dll
4
+ #include "common.h"
5
+
6
+ /*
7
+ =============
8
+ Q_strncpyz
9
+
10
+ Safe strncpy that ensures a trailing zero
11
+ =============
12
+ */
13
+ void Q_strncpyz( char *dest, const char *src, int destsize ) {
14
+ // bk001129 - also NULL dest
15
+ if ( !dest ) {
16
+ Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
17
+ }
18
+ if ( !src ) {
19
+ Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
20
+ }
21
+ if ( destsize < 1 ) {
22
+ Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" );
23
+ }
24
+
25
+ strncpy( dest, src, destsize-1 );
26
+ dest[destsize-1] = 0;
27
+ }
28
+
29
+ int Q_stricmpn (const char *s1, const char *s2, int n) {
30
+ int c1, c2;
31
+
32
+ // bk001129 - moved in 1.17 fix not in id codebase
33
+ if ( s1 == NULL ) {
34
+ if ( s2 == NULL )
35
+ return 0;
36
+ else
37
+ return -1;
38
+ }
39
+ else if ( s2==NULL )
40
+ return 1;
41
+
42
+
43
+
44
+ do {
45
+ c1 = *s1++;
46
+ c2 = *s2++;
47
+
48
+ if (!n--) {
49
+ return 0; // strings are equal until end point
50
+ }
51
+
52
+ if (c1 != c2) {
53
+ if (c1 >= 'a' && c1 <= 'z') {
54
+ c1 -= ('a' - 'A');
55
+ }
56
+ if (c2 >= 'a' && c2 <= 'z') {
57
+ c2 -= ('a' - 'A');
58
+ }
59
+ if (c1 != c2) {
60
+ return c1 < c2 ? -1 : 1;
61
+ }
62
+ }
63
+ } while (c1);
64
+
65
+ return 0; // strings are equal
66
+ }
67
+
68
+ int Q_strncmp (const char *s1, const char *s2, int n) {
69
+ int c1, c2;
70
+
71
+ do {
72
+ c1 = *s1++;
73
+ c2 = *s2++;
74
+
75
+ if (!n--) {
76
+ return 0; // strings are equal until end point
77
+ }
78
+
79
+ if (c1 != c2) {
80
+ return c1 < c2 ? -1 : 1;
81
+ }
82
+ } while (c1);
83
+
84
+ return 0; // strings are equal
85
+ }
86
+
87
+ int Q_stricmp (const char *s1, const char *s2) {
88
+ return (s1 && s2) ? Q_stricmpn (s1, s2, 99999) : -1;
89
+ }
90
+
91
+
92
+ // never goes past bounds or leaves without a terminating 0
93
+ void Q_strcat( char *dest, int size, const char *src ) {
94
+ int l1;
95
+
96
+ l1 = strlen( dest );
97
+ if ( l1 >= size ) {
98
+ Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
99
+ }
100
+ Q_strncpyz( dest + l1, src, size - l1 );
101
+ }
102
+
103
+
104
+ void Com_sprintf( char *dest, int size, const char *fmt, ...) {
105
+ int len;
106
+ va_list argptr;
107
+ char bigbuffer[32000]; // big, but small enough to fit in PPC stack
108
+
109
+ va_start (argptr,fmt);
110
+ len = vsprintf (bigbuffer,fmt,argptr);
111
+ va_end (argptr);
112
+ if ( len >= sizeof( bigbuffer ) ) {
113
+ Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
114
+ }
115
+ if (len >= size) {
116
+ Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
117
+ #ifdef _DEBUG
118
+ __asm {
119
+ int 3;
120
+ }
121
+ #endif
122
+ }
123
+ Q_strncpyz (dest, bigbuffer, size );
124
+ }
125
+
126
+
127
+ /*
128
+ ============
129
+ va
130
+
131
+ does a varargs printf into a temp buffer, so I don't need to have
132
+ varargs versions of all text functions.
133
+ FIXME: make this buffer size safe someday
134
+ ============
135
+ */
136
+ char *va( char *format, ... ) {
137
+ va_list argptr;
138
+ static char string[2][32000]; // in case va is called by nested functions
139
+ static int index = 0;
140
+ char *buf;
141
+
142
+ buf = string[index & 1];
143
+ index++;
144
+
145
+ va_start (argptr, format);
146
+ vsprintf (buf, format,argptr);
147
+ va_end (argptr);
148
+
149
+ return buf;
150
+ }
151
+
152
+
153
+ /*
154
+ =====================================================================
155
+
156
+ INFO STRINGS
157
+
158
+ =====================================================================
159
+ */
160
+
161
+ /*
162
+ ===============
163
+ Info_ValueForKey
164
+
165
+ Searches the string for the given
166
+ key and returns the associated value, or an empty string.
167
+ FIXME: overflow check?
168
+ ===============
169
+ */
170
+ char *Info_ValueForKey( const char *s, const char *key ) {
171
+ char pkey[BIG_INFO_KEY];
172
+ static char value[2][BIG_INFO_VALUE]; // use two buffers so compares
173
+ // work without stomping on each other
174
+ static int valueindex = 0;
175
+ char *o;
176
+
177
+ if ( !s || !key ) {
178
+ return "";
179
+ }
180
+
181
+ if ( strlen( s ) >= BIG_INFO_STRING ) {
182
+ Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
183
+ }
184
+
185
+ valueindex ^= 1;
186
+ if (*s == '\\')
187
+ s++;
188
+ while (1)
189
+ {
190
+ o = pkey;
191
+ while (*s != '\\')
192
+ {
193
+ if (!*s)
194
+ return "";
195
+ *o++ = *s++;
196
+ }
197
+ *o = 0;
198
+ s++;
199
+
200
+ o = value[valueindex];
201
+
202
+ while (*s != '\\' && *s)
203
+ {
204
+ *o++ = *s++;
205
+ }
206
+ *o = 0;
207
+
208
+ if (!Q_stricmp (key, pkey) )
209
+ return value[valueindex];
210
+
211
+ if (!*s)
212
+ break;
213
+ s++;
214
+ }
215
+
216
+ return "";
217
+ }
218
+
219
+
220
+ /*
221
+ ===================
222
+ Info_NextPair
223
+
224
+ Used to itterate through all the key/value pairs in an info string
225
+ ===================
226
+ */
227
+ void Info_NextPair( const char **head, char *key, char *value ) {
228
+ char *o;
229
+ const char *s;
230
+
231
+ s = *head;
232
+
233
+ if ( *s == '\\' ) {
234
+ s++;
235
+ }
236
+ key[0] = 0;
237
+ value[0] = 0;
238
+
239
+ o = key;
240
+ while ( *s != '\\' ) {
241
+ if ( !*s ) {
242
+ *o = 0;
243
+ *head = s;
244
+ return;
245
+ }
246
+ *o++ = *s++;
247
+ }
248
+ *o = 0;
249
+ s++;
250
+
251
+ o = value;
252
+ while ( *s != '\\' && *s ) {
253
+ *o++ = *s++;
254
+ }
255
+ *o = 0;
256
+
257
+ *head = s;
258
+ }
259
+
260
+
261
+ /*
262
+ ===================
263
+ Info_RemoveKey
264
+ ===================
265
+ */
266
+ void Info_RemoveKey( char *s, const char *key ) {
267
+ char *start;
268
+ char pkey[MAX_INFO_KEY];
269
+ char value[MAX_INFO_VALUE];
270
+ char *o;
271
+
272
+ if ( strlen( s ) >= MAX_INFO_STRING ) {
273
+ Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
274
+ }
275
+
276
+ if (strchr (key, '\\')) {
277
+ return;
278
+ }
279
+
280
+ while (1)
281
+ {
282
+ start = s;
283
+ if (*s == '\\')
284
+ s++;
285
+ o = pkey;
286
+ while (*s != '\\')
287
+ {
288
+ if (!*s)
289
+ return;
290
+ *o++ = *s++;
291
+ }
292
+ *o = 0;
293
+ s++;
294
+
295
+ o = value;
296
+ while (*s != '\\' && *s)
297
+ {
298
+ if (!*s)
299
+ return;
300
+ *o++ = *s++;
301
+ }
302
+ *o = 0;
303
+
304
+ if (!strcmp (key, pkey) )
305
+ {
306
+ strcpy (start, s); // remove this part
307
+ return;
308
+ }
309
+
310
+ if (!*s)
311
+ return;
312
+ }
313
+
314
+ }
315
+
316
+ /*
317
+ ===================
318
+ Info_RemoveKey_Big
319
+ ===================
320
+ */
321
+ void Info_RemoveKey_Big( char *s, const char *key ) {
322
+ char *start;
323
+ char pkey[BIG_INFO_KEY];
324
+ char value[BIG_INFO_VALUE];
325
+ char *o;
326
+
327
+ if ( strlen( s ) >= BIG_INFO_STRING ) {
328
+ Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
329
+ }
330
+
331
+ if (strchr (key, '\\')) {
332
+ return;
333
+ }
334
+
335
+ while (1)
336
+ {
337
+ start = s;
338
+ if (*s == '\\')
339
+ s++;
340
+ o = pkey;
341
+ while (*s != '\\')
342
+ {
343
+ if (!*s)
344
+ return;
345
+ *o++ = *s++;
346
+ }
347
+ *o = 0;
348
+ s++;
349
+
350
+ o = value;
351
+ while (*s != '\\' && *s)
352
+ {
353
+ if (!*s)
354
+ return;
355
+ *o++ = *s++;
356
+ }
357
+ *o = 0;
358
+
359
+ if (!strcmp (key, pkey) )
360
+ {
361
+ strcpy (start, s); // remove this part
362
+ return;
363
+ }
364
+
365
+ if (!*s)
366
+ return;
367
+ }
368
+
369
+ }
370
+
371
+
372
+
373
+
374
+ /*
375
+ ==================
376
+ Info_Validate
377
+
378
+ Some characters are illegal in info strings because they
379
+ can mess up the server's parsing
380
+ ==================
381
+ */
382
+ qboolean Info_Validate( const char *s ) {
383
+ if ( strchr( s, '\"' ) ) {
384
+ return qfalse;
385
+ }
386
+ if ( strchr( s, ';' ) ) {
387
+ return qfalse;
388
+ }
389
+ return qtrue;
390
+ }
391
+
392
+ /*
393
+ ==================
394
+ Info_SetValueForKey
395
+
396
+ Changes or adds a key/value pair
397
+ ==================
398
+ */
399
+ void Info_SetValueForKey( char *s, const char *key, const char *value ) {
400
+ char newi[MAX_INFO_STRING];
401
+
402
+ if ( strlen( s ) >= MAX_INFO_STRING ) {
403
+ Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
404
+ }
405
+
406
+ if (strchr (key, '\\') || strchr (value, '\\'))
407
+ {
408
+ Com_Printf ("Can't use keys or values with a \\\n");
409
+ return;
410
+ }
411
+
412
+ if (strchr (key, ';') || strchr (value, ';'))
413
+ {
414
+ Com_Printf ("Can't use keys or values with a semicolon\n");
415
+ return;
416
+ }
417
+
418
+ if (strchr (key, '\"') || strchr (value, '\"'))
419
+ {
420
+ Com_Printf ("Can't use keys or values with a \"\n");
421
+ return;
422
+ }
423
+
424
+ Info_RemoveKey (s, key);
425
+ if (!value || !strlen(value))
426
+ return;
427
+
428
+ Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
429
+
430
+ if (strlen(newi) + strlen(s) > MAX_INFO_STRING)
431
+ {
432
+ Com_Printf ("Info string length exceeded\n");
433
+ return;
434
+ }
435
+
436
+ strcat (newi, s);
437
+ strcpy (s, newi);
438
+ }
439
+
440
+ /*
441
+ ==================
442
+ Info_SetValueForKey_Big
443
+
444
+ Changes or adds a key/value pair
445
+ ==================
446
+ */
447
+ void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
448
+ char newi[BIG_INFO_STRING];
449
+
450
+ if ( strlen( s ) >= BIG_INFO_STRING ) {
451
+ Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
452
+ }
453
+
454
+ if (strchr (key, '\\') || strchr (value, '\\'))
455
+ {
456
+ Com_Printf ("Can't use keys or values with a \\\n");
457
+ return;
458
+ }
459
+
460
+ if (strchr (key, ';') || strchr (value, ';'))
461
+ {
462
+ Com_Printf ("Can't use keys or values with a semicolon\n");
463
+ return;
464
+ }
465
+
466
+ if (strchr (key, '\"') || strchr (value, '\"'))
467
+ {
468
+ Com_Printf ("Can't use keys or values with a \"\n");
469
+ return;
470
+ }
471
+
472
+ Info_RemoveKey_Big (s, key);
473
+ if (!value || !strlen(value))
474
+ return;
475
+
476
+ Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
477
+
478
+ if (strlen(newi) + strlen(s) > BIG_INFO_STRING)
479
+ {
480
+ Com_Printf ("BIG Info string length exceeded\n");
481
+ return;
482
+ }
483
+
484
+ strcat (s, newi);
485
+ }
486
+
487
+
488
+
489
+
490
+ //====================================================================
491
+
492
+ /*
493
+ ============
494
+ Com_Error
495
+ ============
496
+ */
497
+ void Com_Error( errorParm_t level, const char *error, ... ) {
498
+ va_list argptr;
499
+ char buffer[MAXPRINTMSG];
500
+
501
+ va_start( argptr, error );
502
+ vsprintf( buffer, error, argptr );
503
+ va_end( argptr );
504
+
505
+ printf( "*********************\n"
506
+ "ERROR: %s\n"
507
+ "*********************\n", buffer );
508
+ exit( EXIT_FAILURE );
509
+
510
+ }
511
+
512
+ /*
513
+ ============
514
+ Com_Printf
515
+ ============
516
+ */
517
+ void Com_Printf( const char *text, ... ) {
518
+ va_list argptr;
519
+ char buffer[MAXPRINTMSG];
520
+
521
+ va_start( argptr, text );
522
+ vsprintf( buffer, text, argptr );
523
+ va_end( argptr );
524
+
525
+ printf( "%s", buffer );
526
+ }
527
+
528
+ /*
529
+ =================
530
+ CopyString
531
+ =================
532
+ */
533
+ char *CopyString( const char *in ) {
534
+ char *out;
535
+
536
+ if( !in ) {
537
+ return NULL;
538
+ }
539
+
540
+ out = malloc( strlen( in ) + 1 );
541
+ strcpy( out, in );
542
+
543
+ return out;
544
+ }
545
+
546
+
547
+ /*
548
+ ==============
549
+ COM_Parse
550
+
551
+ Parse a token out of a string
552
+ ==============
553
+ */
554
+ char *COM_Parse (char **data_p)
555
+ {
556
+ int c;
557
+ int len;
558
+ char *data;
559
+ static char com_token[MAX_TOKEN_CHARS];
560
+
561
+ data = *data_p;
562
+ len = 0;
563
+ com_token[0] = 0;
564
+
565
+ if (!data)
566
+ {
567
+ *data_p = NULL;
568
+ return "";
569
+ }
570
+
571
+ // skip whitespace
572
+ skipwhite:
573
+ while ( (c = *data) <= ' ')
574
+ {
575
+ if (c == 0)
576
+ {
577
+ *data_p = NULL;
578
+ return "";
579
+ }
580
+ data++;
581
+ }
582
+
583
+ // skip // comments
584
+ if (c=='/' && data[1] == '/')
585
+ {
586
+ while (*data && *data != '\n')
587
+ data++;
588
+ goto skipwhite;
589
+ }
590
+
591
+ // handle quoted strings specially
592
+ if (c == '\"')
593
+ {
594
+ data++;
595
+ while (1)
596
+ {
597
+ c = *data++;
598
+ if (c=='\"' || !c)
599
+ {
600
+ com_token[len] = 0;
601
+ *data_p = data;
602
+ return com_token;
603
+ }
604
+ if (len < MAX_TOKEN_CHARS)
605
+ {
606
+ com_token[len] = c;
607
+ len++;
608
+ }
609
+ }
610
+ }
611
+
612
+ // parse a regular word
613
+ do
614
+ {
615
+ if (len < MAX_TOKEN_CHARS)
616
+ {
617
+ com_token[len] = c;
618
+ len++;
619
+ }
620
+ data++;
621
+ c = *data;
622
+ } while (c>32);
623
+
624
+ if (len == MAX_TOKEN_CHARS)
625
+ {
626
+ // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
627
+ len = 0;
628
+ }
629
+ com_token[len] = 0;
630
+
631
+ *data_p = data;
632
+ return com_token;
633
+ }
634
+
635
+ static int cmd_argc;
636
+ static char *cmd_argv[MAX_STRING_TOKENS];
637
+ static char *cmd_null_string = "";
638
+ static char cmd_args[MAX_STRING_CHARS];
639
+
640
+ /*
641
+ ============
642
+ Cmd_Argc
643
+ ============
644
+ */
645
+ int Cmd_Argc (void)
646
+ {
647
+ return cmd_argc;
648
+ }
649
+
650
+ /*
651
+ ============
652
+ Cmd_Argv
653
+ ============
654
+ */
655
+ char *Cmd_Argv (int arg)
656
+ {
657
+ if ( (unsigned)arg >= cmd_argc )
658
+ return cmd_null_string;
659
+ return cmd_argv[arg];
660
+ }
661
+
662
+ /*
663
+ ============
664
+ Cmd_Args
665
+
666
+ Returns a single string containing argv(1) to argv(argc()-1)
667
+ ============
668
+ */
669
+ char *Cmd_Args (void)
670
+ {
671
+ return cmd_args;
672
+ }
673
+
674
+
675
+ /*
676
+ ============
677
+ Cmd_TokenizeString
678
+
679
+ Parses the given string into command line tokens.
680
+ $Cvars will be expanded unless they are in a quoted token
681
+ ============
682
+ */
683
+ void Cmd_TokenizeString (char *text)
684
+ {
685
+ int i;
686
+ char *com_token;
687
+
688
+ // clear the args from the last string
689
+ for (i=0 ; i<cmd_argc ; i++)
690
+ free (cmd_argv[i]);
691
+
692
+ cmd_argc = 0;
693
+ cmd_args[0] = 0;
694
+
695
+ if (!text)
696
+ return;
697
+
698
+ while (1)
699
+ {
700
+ // skip whitespace up to a /n
701
+ while (*text && *text <= ' ' && *text != '\n')
702
+ {
703
+ text++;
704
+ }
705
+
706
+ if (*text == '\n')
707
+ { // a newline seperates commands in the buffer
708
+ text++;
709
+ break;
710
+ }
711
+
712
+ if (!*text)
713
+ return;
714
+
715
+ // set cmd_args to everything after the first arg
716
+ if (cmd_argc == 1)
717
+ {
718
+ int l;
719
+
720
+ strcpy (cmd_args, text);
721
+
722
+ // strip off any trailing whitespace
723
+ l = strlen(cmd_args) - 1;
724
+ for ( ; l >= 0 ; l--)
725
+ if (cmd_args[l] <= ' ')
726
+ cmd_args[l] = 0;
727
+ else
728
+ break;
729
+ }
730
+
731
+ com_token = COM_Parse (&text);
732
+ if (!text)
733
+ return;
734
+
735
+ if (cmd_argc < MAX_STRING_TOKENS)
736
+ {
737
+ cmd_argv[cmd_argc] = malloc (strlen(com_token)+1);
738
+ strcpy (cmd_argv[cmd_argc], com_token);
739
+ cmd_argc++;
740
+ }
741
+ }
742
+
743
+ }
744
+
745
+
746
+ /*
747
+ ============
748
+ Info_Print
749
+ ============
750
+ */
751
+ void Info_Print( const char *s ) {
752
+ char key[MAXPRINTMSG];
753
+ char value[MAXPRINTMSG];
754
+ char *o;
755
+ int l;
756
+
757
+ if( *s == '\\' ) {
758
+ s++;
759
+ }
760
+
761
+ while( *s )
762
+ {
763
+ o = key;
764
+ while( *s && *s != '\\' ) // && o - key < MAXPRINTMSG )
765
+ {
766
+ *o++ = *s++;
767
+ }
768
+
769
+ l = o - key;
770
+ if( l < 20 ) {
771
+ memset( o, 0, 20-l );
772
+ key[20] = 0;
773
+ } else {
774
+ *o = 0;
775
+ }
776
+ append_result(" %s: ", key);
777
+
778
+ if( !*s ) {
779
+ Com_Printf( "MISSING VALUE\n" );
780
+ return;
781
+ }
782
+
783
+ o = value;
784
+ s++;
785
+ while( *s && *s != '\\' ) // && o - value < MAXPRINTMSG )
786
+ {
787
+ *o++ = *s++;
788
+ }
789
+ *o = 0;
790
+
791
+ if( *s ) {
792
+ s++;
793
+ }
794
+ append_result("\"%s\"\n", value);
795
+ }
796
+ }
797
+