1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "framework/Licensee.h"
31 #include "MayaImport/maya_main.h"
32 
33 #include "gamesys/SysCvar.h"
34 #include "Game_local.h"
35 
36 #include "anim/Anim.h"
37 
38 /***********************************************************************
39 
40 	Maya conversion functions
41 
42 ***********************************************************************/
43 
44 static idStr				Maya_Error;
45 
46 static exporterInterface_t	Maya_ConvertModel = NULL;
47 static exporterShutdown_t	Maya_Shutdown = NULL;
48 static uintptr_t			importDLL = 0;
49 
50 bool idModelExport::initialized = false;
51 
52 /*
53 ====================
54 idModelExport::idModelExport
55 ====================
56 */
idModelExport()57 idModelExport::idModelExport() {
58 	Reset();
59 }
60 
61 /*
62 ====================
63 idModelExport::Shutdown
64 ====================
65 */
Shutdown(void)66 void idModelExport::Shutdown( void ) {
67 	if ( Maya_Shutdown ) {
68 		Maya_Shutdown();
69 	}
70 
71 	if ( importDLL ) {
72 		sys->DLL_Unload( importDLL );
73 	}
74 
75 	importDLL = 0;
76 	Maya_Shutdown = NULL;
77 	Maya_ConvertModel = NULL;
78 	Maya_Error.Clear();
79 	initialized = false;
80 }
81 
82 /*
83 =====================
84 idModelExport::CheckMayaInstall
85 
86 Determines if Maya is installed on the user's machine
87 =====================
88 */
CheckMayaInstall(void)89 bool idModelExport::CheckMayaInstall( void ) {
90 #ifndef _WIN32
91 	return false;
92 #elif 0
93 	HKEY	hKey;
94 	long	lres, lType;
95 
96 	lres = RegOpenKey( HKEY_LOCAL_MACHINE, "SOFTWARE\\Alias|Wavefront\\Maya\\4.5\\Setup\\InstallPath", &hKey );
97 
98 	if ( lres != ERROR_SUCCESS ) {
99 		return false;
100 	}
101 
102 	lres = RegQueryValueEx( hKey, "MAYA_INSTALL_LOCATION", NULL, (unsigned long*)&lType, (unsigned char*)NULL, (unsigned long*)NULL );
103 
104 	RegCloseKey( hKey );
105 
106 	if ( lres != ERROR_SUCCESS ) {
107 		return false;
108 	}
109 	return true;
110 #else
111 	HKEY	hKey;
112 	long	lres;
113 
114 	// only check the non-version specific key so that we only have to update the maya dll when new versions are released
115 	lres = RegOpenKey( HKEY_LOCAL_MACHINE, "SOFTWARE\\Alias|Wavefront\\Maya", &hKey );
116 	RegCloseKey( hKey );
117 
118 	if ( lres != ERROR_SUCCESS ) {
119 		return false;
120 	}
121 	return true;
122 #endif
123 }
124 
125 /*
126 =====================
127 idModelExport::LoadMayaDll
128 
129 Checks to see if we can load the Maya export dll
130 =====================
131 */
LoadMayaDll(void)132 void idModelExport::LoadMayaDll( void ) {
133 	exporterDLLEntry_t	dllEntry;
134 	char				dllPath[ MAX_OSPATH ];
135 
136 	fileSystem->FindDLL( "MayaImport", dllPath );
137 	if ( !dllPath[ 0 ] ) {
138 		return;
139 	}
140 	importDLL = sys->DLL_Load( dllPath );
141 	if ( !importDLL ) {
142 		return;
143 	}
144 
145 	// look up the dll interface functions
146 	dllEntry = ( exporterDLLEntry_t )sys->DLL_GetProcAddress( importDLL, "dllEntry" );
147 	Maya_ConvertModel = ( exporterInterface_t )sys->DLL_GetProcAddress( importDLL, "Maya_ConvertModel" );
148 	Maya_Shutdown = ( exporterShutdown_t )sys->DLL_GetProcAddress( importDLL, "Maya_Shutdown" );
149 	if ( !Maya_ConvertModel || !dllEntry || !Maya_Shutdown ) {
150 		Maya_ConvertModel = NULL;
151 		Maya_Shutdown = NULL;
152 		sys->DLL_Unload( importDLL );
153 		importDLL = 0;
154 		gameLocal.Error( "Invalid interface on export DLL." );
155 		return;
156 	}
157 
158 	// initialize the DLL
159 	if ( !dllEntry( MD5_VERSION, common, sys ) ) {
160 		// init failed
161 		Maya_ConvertModel = NULL;
162 		Maya_Shutdown = NULL;
163 		sys->DLL_Unload( importDLL );
164 		importDLL = 0;
165 		gameLocal.Error( "Export DLL init failed." );
166 		return;
167 	}
168 }
169 
170 /*
171 =====================
172 idModelExport::ConvertMayaToMD5
173 
174 Checks if a Maya model should be converted to an MD5, and converts if if the time/date or
175 version number has changed.
176 =====================
177 */
ConvertMayaToMD5(void)178 bool idModelExport::ConvertMayaToMD5( void ) {
179 	ID_TIME_T		sourceTime;
180 	ID_TIME_T		destTime;
181 	int			version;
182 	idToken		cmdLine;
183 	idStr		path;
184 
185 	// check if our DLL got loaded
186 	if ( initialized && !Maya_ConvertModel ) {
187 		Maya_Error = "MayaImport dll not loaded.";
188 		return false;
189 	}
190 
191 	// if idAnimManager::forceExport is set then we always reexport Maya models
192 	if ( idAnimManager::forceExport ) {
193 		force = true;
194 	}
195 
196 	// get the source file's time
197 	if ( fileSystem->ReadFile( src, NULL, &sourceTime ) < 0 ) {
198 		// source file doesn't exist
199 		return true;
200 	}
201 
202 	// get the destination file's time
203 	if ( !force && ( fileSystem->ReadFile( dest, NULL, &destTime ) >= 0 ) ) {
204 		idParser parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
205 
206 		parser.LoadFile( dest );
207 
208 		// read the file version
209 		if ( parser.CheckTokenString( MD5_VERSION_STRING ) ) {
210 			version = parser.ParseInt();
211 
212 			// check the command line
213 			if ( parser.CheckTokenString( "commandline" ) ) {
214 				parser.ReadToken( &cmdLine );
215 
216 				// check the file time, scale, and version
217 				if ( ( destTime >= sourceTime ) && ( version == MD5_VERSION ) && ( cmdLine == commandLine ) ) {
218 					// don't convert it
219 					return true;
220 				}
221 			}
222 		}
223 	}
224 
225 	// if this is the first time we've been run, check if Maya is installed and load our DLL
226 	if ( !initialized ) {
227 		initialized = true;
228 
229 		if ( !CheckMayaInstall() ) {
230 			Maya_Error = "Maya not installed in registry.";
231 			return false;
232 		}
233 
234 		LoadMayaDll();
235 
236 		// check if our DLL got loaded
237 		if ( !Maya_ConvertModel ) {
238 			Maya_Error = "Could not load MayaImport dll.";
239 			return false;
240 		}
241 	}
242 
243 	// we need to make sure we have a full path, so convert the filename to an OS path
244 	// _D3XP :: we work out of the cdpath, at least until we get Alienbrain
245 	src = fileSystem->RelativePathToOSPath( src, "fs_cdpath" );
246 	dest = fileSystem->RelativePathToOSPath( dest, "fs_cdpath" );
247 
248 	dest.ExtractFilePath( path );
249 	if ( path.Length() ) {
250 		fileSystem->CreateOSPath( path );
251 	}
252 
253 	// get the os path in case it needs to create one
254 	path = fileSystem->RelativePathToOSPath( "", "fs_cdpath" /* _D3XP */ );
255 
256 	common->SetRefreshOnPrint( true );
257 	Maya_Error = Maya_ConvertModel( path, commandLine );
258 	common->SetRefreshOnPrint( false );
259 	if ( Maya_Error != "Ok" ) {
260 		return false;
261 	}
262 
263 	// conversion succeded
264 	return true;
265 }
266 
267 /*
268 ====================
269 idModelExport::Reset
270 ====================
271 */
Reset(void)272 void idModelExport::Reset( void ) {
273 	force		= false;
274 	commandLine = "";
275 	src			= "";
276 	dest		= "";
277 }
278 
279 /*
280 ====================
281 idModelExport::ExportModel
282 ====================
283 */
ExportModel(const char * model)284 bool idModelExport::ExportModel( const char *model ) {
285 	const char *game = cvarSystem->GetCVarString( "fs_game" );
286 	if ( strlen(game) == 0 ) {
287 		game = BASE_GAMEDIR;
288 	}
289 
290 	Reset();
291 	src  = model;
292 	dest = model;
293 	dest.SetFileExtension( MD5_MESH_EXT );
294 
295 	sprintf( commandLine, "mesh %s -dest %s -game %s", src.c_str(), dest.c_str(), game );
296 	if ( !ConvertMayaToMD5() ) {
297 		gameLocal.Printf( "Failed to export '%s' : %s", src.c_str(), Maya_Error.c_str() );
298 		return false;
299 	}
300 
301 	return true;
302 }
303 
304 /*
305 ====================
306 idModelExport::ExportAnim
307 ====================
308 */
ExportAnim(const char * anim)309 bool idModelExport::ExportAnim( const char *anim ) {
310 	const char *game = cvarSystem->GetCVarString( "fs_game" );
311 	if ( strlen(game) == 0 ) {
312 		game = BASE_GAMEDIR;
313 	}
314 
315 	Reset();
316 	src  = anim;
317 	dest = anim;
318 	dest.SetFileExtension( MD5_ANIM_EXT );
319 
320 	sprintf( commandLine, "anim %s -dest %s -game %s", src.c_str(), dest.c_str(), game );
321 	if ( !ConvertMayaToMD5() ) {
322 		gameLocal.Printf( "Failed to export '%s' : %s", src.c_str(), Maya_Error.c_str() );
323 		return false;
324 	}
325 
326 	return true;
327 }
328 
329 /*
330 ====================
331 idModelExport::ParseOptions
332 ====================
333 */
ParseOptions(idLexer & lex)334 bool idModelExport::ParseOptions( idLexer &lex ) {
335 	idToken	token;
336 	idStr	destdir;
337 	idStr	sourcedir;
338 
339 	if ( !lex.ReadToken( &token ) ) {
340 		lex.Error( "Expected filename" );
341 		return false;
342 	}
343 
344 	src = token;
345 	dest = token;
346 
347 	while( lex.ReadToken( &token ) ) {
348 		if ( token == "-" ) {
349 			if ( !lex.ReadToken( &token ) ) {
350 				lex.Error( "Expecting option" );
351 				return false;
352 			}
353 			if ( token == "sourcedir" ) {
354 				if ( !lex.ReadToken( &token ) ) {
355 					lex.Error( "Missing pathname after -sourcedir" );
356 					return false;
357 				}
358 				sourcedir = token;
359 			} else if ( token == "destdir" ) {
360 				if ( !lex.ReadToken( &token ) ) {
361 					lex.Error( "Missing pathname after -destdir" );
362 					return false;
363 				}
364 				destdir = token;
365 			} else if ( token == "dest" ) {
366 				if ( !lex.ReadToken( &token ) ) {
367 					lex.Error( "Missing filename after -dest" );
368 					return false;
369 				}
370 				dest = token;
371 			} else {
372 				commandLine += va( " -%s", token.c_str() );
373 			}
374 		} else {
375 			commandLine += va( " %s", token.c_str() );
376 		}
377 	}
378 
379 	if ( sourcedir.Length() ) {
380 		src.StripPath();
381 		sourcedir.BackSlashesToSlashes();
382 		sprintf( src, "%s/%s", sourcedir.c_str(), src.c_str() );
383 	}
384 
385 	if ( destdir.Length() ) {
386 		dest.StripPath();
387 		destdir.BackSlashesToSlashes();
388 		sprintf( dest, "%s/%s", destdir.c_str(), dest.c_str() );
389 	}
390 
391 	return true;
392 }
393 
394 /*
395 ====================
396 idModelExport::ParseExportSection
397 ====================
398 */
ParseExportSection(idParser & parser)399 int idModelExport::ParseExportSection( idParser &parser ) {
400 	idToken	command;
401 	idToken	token;
402 	idStr	defaultCommands;
403 	idLexer lex;
404 	idStr	temp;
405 	idStr	parms;
406 	int		count;
407 
408 	const char *game = cvarSystem->GetCVarString( "fs_game" );
409 
410 	if ( strlen(game) == 0 ) {
411 		game = BASE_GAMEDIR;
412 	}
413 
414 	// only export sections that match our export mask
415 	if ( g_exportMask.GetString()[ 0 ] ) {
416 		if ( parser.CheckTokenString( "{" ) ) {
417 			parser.SkipBracedSection( false );
418 			return 0;
419 		}
420 
421 		parser.ReadToken( &token );
422 		if ( token.Icmp( g_exportMask.GetString() ) ) {
423 			parser.SkipBracedSection();
424 			return 0;
425 		}
426 		parser.ExpectTokenString( "{" );
427 	} else if ( !parser.CheckTokenString( "{" ) ) {
428 		// skip the export mask
429 		parser.ReadToken( &token );
430 		parser.ExpectTokenString( "{" );
431 	}
432 
433 	count = 0;
434 
435 	lex.SetFlags( LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
436 
437 	while( 1 ) {
438 
439 		if ( !parser.ReadToken( &command ) ) {
440 			parser.Error( "Unexpoected end-of-file" );
441 			break;
442 		}
443 
444 		if ( command == "}" ) {
445 			break;
446 		}
447 
448 		if ( command == "options" ) {
449 			parser.ParseRestOfLine( defaultCommands );
450 		} else if ( command == "addoptions" ) {
451 			parser.ParseRestOfLine( temp );
452 			defaultCommands += " ";
453 			defaultCommands += temp;
454 		} else if ( ( command == "mesh" ) || ( command == "anim" ) || ( command == "camera" ) ) {
455 			if ( !parser.ReadToken( &token ) ) {
456 				parser.Error( "Expected filename" );
457 			}
458 
459 			temp = token;
460 			parser.ParseRestOfLine( parms );
461 
462 			if ( defaultCommands.Length() ) {
463 				sprintf( temp, "%s %s", temp.c_str(), defaultCommands.c_str() );
464 			}
465 
466 			if ( parms.Length() ) {
467 				sprintf( temp, "%s %s", temp.c_str(), parms.c_str() );
468 			}
469 
470 			lex.LoadMemory( temp, temp.Length(), parser.GetFileName() );
471 
472 			Reset();
473 			if ( ParseOptions( lex ) ) {
474 				const char *game = cvarSystem->GetCVarString( "fs_game" );
475 				if ( strlen(game) == 0 ) {
476 					game = BASE_GAMEDIR;
477 				}
478 
479 				if ( command == "mesh" ) {
480 					dest.SetFileExtension( MD5_MESH_EXT );
481 				} else if ( command == "anim" ) {
482 					dest.SetFileExtension( MD5_ANIM_EXT );
483 				} else if ( command == "camera" ) {
484 					dest.SetFileExtension( MD5_CAMERA_EXT );
485 				} else {
486 					dest.SetFileExtension( command );
487 				}
488 				idStr back = commandLine;
489 				sprintf( commandLine, "%s %s -dest %s -game %s%s", command.c_str(), src.c_str(), dest.c_str(), game, commandLine.c_str() );
490 				if ( ConvertMayaToMD5() ) {
491 					count++;
492 				} else {
493 					parser.Warning( "Failed to export '%s' : %s", src.c_str(), Maya_Error.c_str() );
494 				}
495 			}
496 			lex.FreeSource();
497 		} else {
498 			parser.Error( "Unknown token: %s", command.c_str() );
499 			parser.SkipBracedSection( false );
500 			break;
501 		}
502 	}
503 
504 	return count;
505 }
506 
507 /*
508 ================
509 idModelExport::ExportDefFile
510 ================
511 */
ExportDefFile(const char * filename)512 int idModelExport::ExportDefFile( const char *filename ) {
513 	idParser	parser( LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
514 	idToken		token;
515 	int			count;
516 
517 	count = 0;
518 
519 	if ( !parser.LoadFile( filename ) ) {
520 		gameLocal.Printf( "Could not load '%s'\n", filename );
521 		return 0;
522 	}
523 
524 	while( parser.ReadToken( &token ) ) {
525 		if ( token == "export" ) {
526 			count += ParseExportSection( parser );
527 		} else {
528 			parser.ReadToken( &token );
529 			parser.SkipBracedSection();
530 		}
531 	}
532 
533 	return count;
534 }
535 
536 /*
537 ================
538 idModelExport::ExportModels
539 ================
540 */
ExportModels(const char * pathname,const char * extension)541 int idModelExport::ExportModels( const char *pathname, const char *extension ) {
542 	int	count;
543 
544 	count = 0;
545 
546 	idFileList *files;
547 	int			i;
548 
549 	if ( !CheckMayaInstall() ) {
550 		// if Maya isn't installed, don't bother checking if we have anims to export
551 		return 0;
552 	}
553 
554 	gameLocal.Printf( "----- Exporting models -----\n" );
555 	if ( !g_exportMask.GetString()[ 0 ] ) {
556 		gameLocal.Printf( "  Export mask: '%s'\n", g_exportMask.GetString() );
557 	}
558 
559 	count = 0;
560 
561 	files = fileSystem->ListFiles( pathname, extension );
562 	for( i = 0; i < files->GetNumFiles(); i++ ) {
563 		count += ExportDefFile( va( "%s/%s", pathname, files->GetFile( i ) ) );
564 	}
565 	fileSystem->FreeFileList( files );
566 
567 	gameLocal.Printf( "...%d models exported.\n", count );
568 
569 	return count;
570 }
571