1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6
7 This file is part of the OpenJK source code.
8
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12
13 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22
23 // tr_image.c
24 #include "tr_local.h"
25
26 #include <map>
27
28 bool gServerSkinHack = false;
29
30 shader_t *R_FindServerShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage );
31 static char *CommaParse( char **data_p );
32 /*
33 ===============
34 RE_SplitSkins
35 input = skinname, possibly being a macro for three skins
36 return= true if three part skins found
37 output= qualified names to three skins if return is true, undefined if false
38 ===============
39 */
RE_SplitSkins(const char * INname,char * skinhead,char * skintorso,char * skinlower)40 bool RE_SplitSkins(const char *INname, char *skinhead, char *skintorso, char *skinlower)
41 { //INname= "models/players/jedi_tf/|head01_skin1|torso01|lower01";
42 if (strchr(INname, '|'))
43 {
44 char name[MAX_QPATH];
45 strcpy(name, INname);
46 char *p = strchr(name, '|');
47 *p=0;
48 p++;
49 //fill in the base path
50 strcpy (skinhead, name);
51 strcpy (skintorso, name);
52 strcpy (skinlower, name);
53
54 //now get the the individual files
55
56 //advance to second
57 char *p2 = strchr(p, '|');
58 assert(p2);
59 if (!p2)
60 {
61 return false;
62 }
63 *p2=0;
64 p2++;
65 strcat (skinhead, p);
66 strcat (skinhead, ".skin");
67
68
69 //advance to third
70 p = strchr(p2, '|');
71 assert(p);
72 if (!p)
73 {
74 return false;
75 }
76 *p=0;
77 p++;
78 strcat (skintorso,p2);
79 strcat (skintorso, ".skin");
80
81 strcat (skinlower,p);
82 strcat (skinlower, ".skin");
83
84 return true;
85 }
86 return false;
87 }
88
89 // given a name, go get the skin we want and return
RE_RegisterIndividualSkin(const char * name,qhandle_t hSkin)90 qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin)
91 {
92 skin_t *skin;
93 skinSurface_t *surf;
94 char *text, *text_p;
95 char *token;
96 char surfName[MAX_QPATH];
97
98 // load and parse the skin file
99 ri.FS_ReadFile( name, (void **)&text );
100 if ( !text ) {
101 #ifndef FINAL_BUILD
102 Com_Printf( "WARNING: RE_RegisterSkin( '%s' ) failed to load!\n", name );
103 #endif
104 return 0;
105 }
106
107 assert (tr.skins[hSkin]); //should already be setup, but might be an 3part append
108
109 skin = tr.skins[hSkin];
110
111 text_p = text;
112 while ( text_p && *text_p ) {
113 // get surface name
114 token = CommaParse( &text_p );
115 Q_strncpyz( surfName, token, sizeof( surfName ) );
116
117 if ( !token[0] ) {
118 break;
119 }
120 // lowercase the surface name so skin compares are faster
121 Q_strlwr( surfName );
122
123 if ( *text_p == ',' ) {
124 text_p++;
125 }
126
127 if ( !strncmp( token, "tag_", 4 ) ) { //these aren't in there, but just in case you load an id style one...
128 continue;
129 }
130
131 // parse the shader name
132 token = CommaParse( &text_p );
133
134 if ( !strcmp( &surfName[strlen(surfName)-4], "_off") )
135 {
136 if ( !strcmp( token ,"*off" ) )
137 {
138 continue; //don't need these double offs
139 }
140 surfName[strlen(surfName)-4] = 0; //remove the "_off"
141 }
142 if ((int)(sizeof( skin->surfaces) / sizeof( skin->surfaces[0] )) <= skin->numSurfaces)
143 {
144 assert( (int)(sizeof( skin->surfaces) / sizeof( skin->surfaces[0] )) > skin->numSurfaces );
145 Com_Printf( "WARNING: RE_RegisterSkin( '%s' ) more than %u surfaces!\n", name, (unsigned int)ARRAY_LEN(skin->surfaces) );
146 break;
147 }
148 surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
149 skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf;
150
151 Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
152
153 if (gServerSkinHack) surf->shader = R_FindServerShader( token, lightmapsNone, stylesDefault, qtrue );
154 else surf->shader = R_FindShader( token, lightmapsNone, stylesDefault, qtrue );
155 skin->numSurfaces++;
156 }
157
158 ri.FS_FreeFile( text );
159
160
161 // never let a skin have 0 shaders
162 if ( skin->numSurfaces == 0 ) {
163 return 0; // use default skin
164 }
165
166 return hSkin;
167 }
168
RE_RegisterSkin(const char * name)169 qhandle_t RE_RegisterSkin( const char *name ) {
170 qhandle_t hSkin;
171 skin_t *skin;
172
173 if ( !name || !name[0] ) {
174 Com_Printf( "Empty name passed to RE_RegisterSkin\n" );
175 return 0;
176 }
177
178 if ( strlen( name ) >= MAX_QPATH ) {
179 Com_Printf( "Skin name exceeds MAX_QPATH\n" );
180 return 0;
181 }
182
183 // see if the skin is already loaded
184 for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) {
185 skin = tr.skins[hSkin];
186 if ( !Q_stricmp( skin->name, name ) ) {
187 if( skin->numSurfaces == 0 ) {
188 return 0; // default skin
189 }
190 return hSkin;
191 }
192 }
193
194 // allocate a new skin
195 if ( tr.numSkins == MAX_SKINS ) {
196 Com_Printf( "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name );
197 return 0;
198 }
199 tr.numSkins++;
200 skin = (struct skin_s *)Hunk_Alloc( sizeof( skin_t ), h_low );
201 tr.skins[hSkin] = skin;
202 Q_strncpyz( skin->name, name, sizeof( skin->name ) );
203 skin->numSurfaces = 0;
204
205 // make sure the render thread is stopped
206 R_IssuePendingRenderCommands();
207
208 // If not a .skin file, load as a single shader
209 if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
210 /* skin->numSurfaces = 1;
211 skin->surfaces[0] = (skinSurface_t *)Hunk_Alloc( sizeof(skin->surfaces[0]), h_low );
212 skin->surfaces[0]->shader = R_FindShader( name, lightmapsNone, stylesDefault, qtrue );
213 return hSkin;
214 */
215 }
216
217 char skinhead[MAX_QPATH]={0};
218 char skintorso[MAX_QPATH]={0};
219 char skinlower[MAX_QPATH]={0};
220 if ( RE_SplitSkins(name, (char*)&skinhead, (char*)&skintorso, (char*)&skinlower ) )
221 {//three part
222 hSkin = RE_RegisterIndividualSkin(skinhead, hSkin);
223 if (hSkin)
224 {
225 hSkin = RE_RegisterIndividualSkin(skintorso, hSkin);
226 if (hSkin)
227 {
228 hSkin = RE_RegisterIndividualSkin(skinlower, hSkin);
229 }
230 }
231 }
232 else
233 {//single skin
234 hSkin = RE_RegisterIndividualSkin(name, hSkin);
235 }
236 return(hSkin);
237 }
238
239
240
241 /*
242 ==================
243 CommaParse
244
245 This is unfortunate, but the skin files aren't
246 compatible with our normal parsing rules.
247 ==================
248 */
CommaParse(char ** data_p)249 static char *CommaParse( char **data_p ) {
250 int c = 0, len;
251 char *data;
252 static char com_token[MAX_TOKEN_CHARS];
253
254 data = *data_p;
255 len = 0;
256 com_token[0] = 0;
257
258 // make sure incoming data is valid
259 if ( !data ) {
260 *data_p = NULL;
261 return com_token;
262 }
263
264 while ( 1 ) {
265 // skip whitespace
266 while( (c = *(const unsigned char* /*eurofix*/)data) <= ' ') {
267 if( !c ) {
268 break;
269 }
270 data++;
271 }
272
273
274 c = *data;
275
276 // skip double slash comments
277 if ( c == '/' && data[1] == '/' )
278 {
279 while (*data && *data != '\n')
280 data++;
281 }
282 // skip /* */ comments
283 else if ( c=='/' && data[1] == '*' )
284 {
285 while ( *data && ( *data != '*' || data[1] != '/' ) )
286 {
287 data++;
288 }
289 if ( *data )
290 {
291 data += 2;
292 }
293 }
294 else
295 {
296 break;
297 }
298 }
299
300 if ( c == 0 ) {
301 return "";
302 }
303
304 // handle quoted strings
305 if (c == '\"')
306 {
307 data++;
308 while (1)
309 {
310 c = *data++;
311 if (c=='\"' || !c)
312 {
313 com_token[len] = 0;
314 *data_p = ( char * ) data;
315 return com_token;
316 }
317 if (len < MAX_TOKEN_CHARS - 1)
318 {
319 com_token[len] = c;
320 len++;
321 }
322 }
323 }
324
325 // parse a regular word
326 do
327 {
328 if (len < MAX_TOKEN_CHARS - 1)
329 {
330 com_token[len] = c;
331 len++;
332 }
333 data++;
334 c = *data;
335 } while (c>32 && c != ',' );
336
337 com_token[len] = 0;
338
339 *data_p = ( char * ) data;
340 return com_token;
341 }
342
343 /*
344 ===============
345 RE_RegisterServerSkin
346
347 Mangled version of the above function to load .skin files on the server.
348 ===============
349 */
RE_RegisterServerSkin(const char * name)350 qhandle_t RE_RegisterServerSkin( const char *name ) {
351 qhandle_t r;
352
353 if (ri.Cvar_VariableIntegerValue( "cl_running" ) &&
354 ri.Com_TheHunkMarkHasBeenMade() &&
355 ShaderHashTableExists())
356 { //If the client is running then we can go straight into the normal registerskin func
357 return RE_RegisterSkin(name);
358 }
359
360 gServerSkinHack = true;
361 r = RE_RegisterSkin(name);
362 gServerSkinHack = false;
363
364 return r;
365 }
366
367 /*
368 ===============
369 R_InitSkins
370 ===============
371 */
R_InitSkins(void)372 void R_InitSkins( void ) {
373 skin_t *skin;
374
375 tr.numSkins = 1;
376
377 // make the default skin have all default shaders
378 skin = tr.skins[0] = (struct skin_s *)ri.Hunk_Alloc( sizeof( skin_t ), h_low );
379 Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
380 skin->numSurfaces = 1;
381 skin->surfaces[0] = (_skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low );
382 skin->surfaces[0]->shader = tr.defaultShader;
383 }
384
385 /*
386 ===============
387 R_GetSkinByHandle
388 ===============
389 */
R_GetSkinByHandle(qhandle_t hSkin)390 skin_t *R_GetSkinByHandle( qhandle_t hSkin ) {
391 if ( hSkin < 1 || hSkin >= tr.numSkins ) {
392 return tr.skins[0];
393 }
394 return tr.skins[ hSkin ];
395 }
396