/* Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "qdata.h" #include "inout.h" #include "md4.h" qboolean g_compress_pak; qboolean g_release; // don't grab, copy output data to new tree qboolean g_pak; // if true, copy to pak instead of release char g_releasedir[1024]; // c:\quake2\baseq2, etc qboolean g_archive; // don't grab, copy source data to new tree qboolean do3ds; char g_only[256]; // if set, only grab this cd qboolean g_skipmodel; // set true when a cd is not g_only char *ext_3ds = "3ds"; char *ext_tri = "tri"; char *trifileext; char game[64] = "quake2"; void InitPaths( int *argc, char **argv ); /* ======================================================= PAK FILES ======================================================= */ typedef struct { char name[56]; int filepos, filelen; } packfile_t; typedef struct { char id[4]; int dirofs; int dirlen; } packheader_t; packfile_t pfiles[16384]; FILE *pakfile; packfile_t *pf; packheader_t pakheader; /* ============== BeginPak ============== */ void BeginPak( char *outname ){ if ( !g_pak ) { return; } pakfile = SafeOpenWrite( outname ); // leave space for header SafeWrite( pakfile, &pakheader, sizeof( pakheader ) ); pf = pfiles; } /* ============== ReleaseFile Filename should be gamedir reletive. Either copies the file to the release dir, or adds it to the pak file. ============== */ void ReleaseFile( char *filename ){ int len; byte *buf; char source[1024]; char dest[1024]; if ( !g_release ) { return; } sprintf( source, "%s%s", gamedir, filename ); if ( !g_pak ) { // copy it sprintf( dest, "%s/%s", g_releasedir, filename ); printf( "copying to %s\n", dest ); QCopyFile( source, dest ); return; } // pak it printf( "paking %s\n", filename ); if ( strlen( filename ) >= sizeof( pf->name ) ) { Error( "Filename too long for pak: %s", filename ); } len = LoadFile( source, (void **)&buf ); if ( g_compress_pak && len < 4096 * 1024 ) { cblock_t in, out; cblock_t Huffman( cblock_t in ); in.count = len; in.data = buf; out = Huffman( in ); if ( out.count < in.count ) { printf( " compressed from %i to %i\n", in.count, out.count ); free( in.data ); buf = out.data; len = out.count; } else{ free( out.data ); } } strcpy( pf->name, filename ); pf->filepos = LittleLong( ftell( pakfile ) ); pf->filelen = LittleLong( len ); pf++; SafeWrite( pakfile, buf, len ); free( buf ); } /* ============== FinishPak ============== */ void FinishPak( void ){ int dirlen; int d; int i; unsigned checksum; if ( !g_pak ) { return; } pakheader.id[0] = 'P'; pakheader.id[1] = 'A'; pakheader.id[2] = 'C'; pakheader.id[3] = 'K'; dirlen = (byte *)pf - (byte *)pfiles; pakheader.dirofs = LittleLong( ftell( pakfile ) ); pakheader.dirlen = LittleLong( dirlen ); checksum = Com_BlockChecksum( (void *)pfiles, dirlen ); SafeWrite( pakfile, pfiles, dirlen ); i = ftell( pakfile ); fseek( pakfile, 0, SEEK_SET ); SafeWrite( pakfile, &pakheader, sizeof( pakheader ) ); fclose( pakfile ); d = pf - pfiles; printf( "%i files packed in %i bytes\n",d, i ); printf( "checksum: 0x%x\n", checksum ); } /* =============== Cmd_File This is only used to cause a file to be copied during a release build (default.cfg, maps, etc) =============== */ void Cmd_File( void ){ GetToken( false ); ReleaseFile( token ); } /* =============== PackDirectory_r =============== */ #if GDEF_OS_WINDOWS #include "io.h" void PackDirectory_r( char *dir ){ struct _finddata_t fileinfo; int handle; char dirstring[1024]; char filename[1024]; sprintf( dirstring, "%s%s/*.*", gamedir, dir ); handle = _findfirst( dirstring, &fileinfo ); if ( handle == -1 ) { return; } do { sprintf( filename, "%s/%s", dir, fileinfo.name ); if ( fileinfo.attrib & _A_SUBDIR ) { // directory if ( fileinfo.name[0] != '.' ) { // don't pak . and .. PackDirectory_r( filename ); } continue; } // copy or pack the file ReleaseFile( filename ); } while ( _findnext( handle, &fileinfo ) != -1 ); _findclose( handle ); } #else #include #include void PackDirectory_r( char *dir ){ #ifdef NeXT struct direct **namelist, *ent; #else struct dirent **namelist, *ent; #endif int count; struct stat st; int i; int len; char fullname[1024]; char dirstring[1024]; char *name; sprintf( dirstring, "%s%s", gamedir, dir ); count = scandir( dirstring, &namelist, NULL, NULL ); for ( i = 0 ; i < count ; i++ ) { ent = namelist[i]; name = ent->d_name; if ( name[0] == '.' ) { continue; } sprintf( fullname, "%s/%s", dir, name ); sprintf( dirstring, "%s%s/%s", gamedir, dir, name ); if ( stat( dirstring, &st ) == -1 ) { Error( "fstating %s", pf->name ); } if ( st.st_mode & S_IFDIR ) { // directory PackDirectory_r( fullname ); continue; } // copy or pack the file ReleaseFile( fullname ); } } #endif /* =============== Cmd_Dir This is only used to cause a directory to be copied during a release build (sounds, etc) =============== */ void Cmd_Dir( void ){ GetToken( false ); PackDirectory_r( token ); } //======================================================================== #define MAX_RTEX 16384 int numrtex; char rtex[MAX_RTEX][64]; void ReleaseTexture( char *name ){ int i; char path[1024]; for ( i = 0 ; i < numrtex ; i++ ) if ( !Q_strncasecmp( name, rtex[i], strlen( name ) ) ) { return; } if ( numrtex == MAX_RTEX ) { Error( "numrtex == MAX_RTEX" ); } strcpy( rtex[i], name ); numrtex++; sprintf( path, "textures/%s.wal", name ); ReleaseFile( path ); } /* =============== Cmd_Maps Only relevent for release and pak files. Releases the .bsp files for the maps, and scans all of the files to build a list of all textures used, which are then released. =============== */ void Cmd_Maps( void ){ char map[1024]; int i; while ( TokenAvailable() ) { GetToken( false ); sprintf( map, "maps/%s.bsp", token ); ReleaseFile( map ); if ( !g_release ) { continue; } // get all the texture references sprintf( map, "%smaps/%s.bsp", gamedir, token ); LoadBSPFileTexinfo( map ); for ( i = 0 ; i < numtexinfo ; i++ ) ReleaseTexture( texinfo[i].texture ); } } //============================================================== /* =============== ParseScript =============== */ void ParseScript( void ){ while ( 1 ) { do { // look for a line starting with a $ command GetToken( true ); if ( endofscript ) { return; } if ( token[0] == '$' ) { break; } while ( TokenAvailable() ) GetToken( false ); } while ( 1 ); // // model commands // if ( !strcmp( token, "$modelname" ) ) { Cmd_Modelname(); } else if ( !strcmp( token, "$base" ) ) { Cmd_Base(); } else if ( !strcmp( token, "$cd" ) ) { Cmd_Cd(); } else if ( !strcmp( token, "$origin" ) ) { Cmd_Origin(); } else if ( !strcmp( token, "$scale" ) ) { Cmd_ScaleUp(); } else if ( !strcmp( token, "$frame" ) ) { Cmd_Frame(); } else if ( !strcmp( token, "$skin" ) ) { Cmd_Skin(); } else if ( !strcmp( token, "$skinsize" ) ) { Cmd_Skinsize(); } // // sprite commands // else if ( !strcmp( token, "$spritename" ) ) { Cmd_SpriteName(); } else if ( !strcmp( token, "$load" ) ) { Cmd_Load(); } else if ( !strcmp( token, "$spriteframe" ) ) { Cmd_SpriteFrame(); } // // image commands // else if ( !strcmp( token, "$grab" ) ) { Cmd_Grab(); } else if ( !strcmp( token, "$raw" ) ) { Cmd_Raw(); } else if ( !strcmp( token, "$colormap" ) ) { Cmd_Colormap(); } else if ( !strcmp( token, "$mippal" ) ) { Cmd_Mippal(); } else if ( !strcmp( token, "$mipdir" ) ) { Cmd_Mipdir(); } else if ( !strcmp( token, "$mip" ) ) { Cmd_Mip(); } else if ( !strcmp( token, "$environment" ) ) { Cmd_Environment(); } // // video // else if ( !strcmp( token, "$video" ) ) { Cmd_Video(); } // // misc // else if ( !strcmp( token, "$file" ) ) { Cmd_File(); } else if ( !strcmp( token, "$dir" ) ) { Cmd_Dir(); } else if ( !strcmp( token, "$maps" ) ) { Cmd_Maps(); } else if ( !strcmp( token, "$alphalight" ) ) { Cmd_Alphalight(); } else if ( !strcmp( token, "$inverse16table" ) ) { Cmd_Inverse16Table(); } else{ Error( "bad command %s\n", token ); } } } //======================================================= /* ============== main ============== */ int main( int argc, char **argv ){ static int i; // VC4.2 compiler bug if auto... char path[1024]; ExpandWildcards( &argc, &argv ); InitPaths( &argc, argv ); for ( i = 1 ; i < argc ; i++ ) { if ( !strcmp( argv[i], "-archive" ) ) { // -archive f:/quake2/release/dump_11_30 archive = true; strcpy( archivedir, argv[i + 1] ); printf( "Archiving source to: %s\n", archivedir ); i++; } else if ( !strcmp( argv[i], "-release" ) ) { g_release = true; strcpy( g_releasedir, argv[i + 1] ); printf( "Copy output to: %s\n", g_releasedir ); i++; } else if ( !strcmp( argv[i], "-compress" ) ) { g_compress_pak = true; printf( "Compressing pakfile\n" ); } else if ( !strcmp( argv[i], "-pak" ) ) { g_release = true; g_pak = true; printf( "Building pakfile: %s\n", argv[i + 1] ); BeginPak( argv[i + 1] ); i++; } else if ( !strcmp( argv[i], "-only" ) ) { strcpy( g_only, argv[i + 1] ); printf( "Only grabbing %s\n", g_only ); i++; } else if ( !strcmp( argv[i], "-3ds" ) ) { do3ds = true; printf( "loading .3ds files\n" ); } else if ( argv[i][0] == '-' ) { Error( "Unknown option \"%s\"", argv[i] ); } else{ break; } } if ( i >= argc ) { Error( "usage: %s [-archive ] [-release ] [-only ] [-3ds] file.qgr", argv[ 0 ] ); } if ( do3ds ) { trifileext = ext_3ds; } else{ trifileext = ext_tri; } for ( ; i < argc ; i++ ) { printf( "--------------- %s ---------------\n", argv[i] ); // load the script strcpy( path, argv[i] ); DefaultExtension( path, ".qdt" ); SetQdirFromPath( path ); LoadScriptFile( ExpandArg( path ) ); // // parse it // ParseScript(); // write out the last model FinishModel(); FinishSprite(); } if ( g_pak ) { FinishPak(); } return 0; }