demo-reader 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/README.rdoc +1 -1
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/demo-reader.gemspec +30 -7
- data/ext/dm68/README.txt +14 -0
- data/ext/dm68/common.c +797 -0
- data/ext/dm68/common.h +568 -0
- data/ext/dm68/dm68.c +57 -0
- data/ext/dm68/dump.c +196 -0
- data/ext/dm68/extconf.rb +16 -0
- data/ext/dm68/huff.c +542 -0
- data/ext/dm68/huff.h +34 -0
- data/ext/dm68/main.c +175 -0
- data/ext/dm68/main.h +126 -0
- data/ext/dm68/msg.c +1082 -0
- data/ext/dm68/msg.h +114 -0
- data/ext/dm68/parse.c +393 -0
- data/lib/demo-reader.rb +11 -3
- data/lib/demo_reader_defrag.rb +106 -0
- data/lib/demo_reader_warsow.rb +3 -5
- data/test/demo_reader_defrag_dm_68_cpm_test.rb +29 -0
- data/test/demo_reader_defrag_dm_68_vq3_test.rb +29 -0
- data/test/fixtures/defrag/dm_68/cpm/pornchronostar_mdf.cpm_00.49.216_tyaz.germany.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/cpm/puremotion_df.cpm_00.10.600_eS-Rody.russia.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/vq3/runkull2_df.vq3_01.05.904_XunderBIRD.Germany.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/vq3/un-dead029_df.vq3_00.16.912_uN-DeaD!WiNTeR.ru.dm_68 +0 -0
- metadata +43 -9
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
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
|
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
|
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-
|
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.
|
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/
|
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::
|
92
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
70
93
|
else
|
71
94
|
end
|
72
95
|
else
|
data/ext/dm68/README.txt
ADDED
@@ -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
|
+
|