mirror of
https://github.com/Garux/netradiant-custom.git
synced 2026-09-28 00:00:03 +02:00
pack all found sky shaders WARNING444 on texture reuse MAX_SHADER_INFO bumped load all uniquely named shader files, only from folders, not packs VFS mod to not keep pk3s opened (avoid hitting OS half opened files limit)(risky and slow for normal use) -packDups: vfsPackSkyImage: pack only new images, referring to main and dups DBs; pack alt image versions to separate pk3s vfsFindSkyFiles: pack sky images, not referenced by any shader and not present in main DB and dups DB (crc check) normal vfsPackFile: split output pk3s to approximately 2 GB chunks -packQer: also pack qer_editorimage -validateShaders: useful comments about referenced textures state right in out shader, do not save entirely invalid shaders (no packaging) -shaderDups: write similar shaders to files for latter evaluation by diff tool (no packaging) WARNING666 when equally named shaders reference different sky boxes
1297 lines
38 KiB
C++
1297 lines
38 KiB
C++
/* -------------------------------------------------------------------------------
|
|
|
|
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
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
This code has been altered significantly from its original form, to support
|
|
several games based on the Quake III Arena engine, in the form of "Q3Map2."
|
|
|
|
------------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "q3map2.h"
|
|
#include "autopk3.h"
|
|
#include "shaders.h"
|
|
#include <map>
|
|
|
|
|
|
using StrList = std::vector<String64>;
|
|
|
|
inline const String64 *StrList_find( const StrList& list, const char* string ){
|
|
for ( auto&& s : list ){
|
|
if ( striEqual( s, string ) )
|
|
return &s;
|
|
}
|
|
return nullptr;
|
|
}
|
|
inline String64 *StrList_find( StrList& list, const char* string ){
|
|
return const_cast<String64*>( StrList_find( const_cast<const StrList&>( list ), string ) );
|
|
}
|
|
|
|
|
|
/*
|
|
Append newcoming texture, if it is unique and not excluded
|
|
*/
|
|
static inline void tex2list( StrList& texlist, const StrList& EXtex, const StrList* rEXtex ){
|
|
if ( strEmpty( token ) )
|
|
return;
|
|
//StripExtension( token );
|
|
char* dot = path_get_filename_base_end( token );
|
|
if( striEqual( dot, ".tga" ) || striEqual( dot, ".jpg" ) || striEqual( dot, ".png" ) ){ //? might want to also warn on png in non png run
|
|
strClear( dot );
|
|
}
|
|
else{
|
|
Sys_FPrintf( SYS_WRN, "WARNING4: %s : weird or missing extension in shader image path\n", token );
|
|
}
|
|
|
|
FixDOSName( token );
|
|
|
|
/* exclude */
|
|
if( !StrList_find( texlist, token ) &&
|
|
!StrList_find( EXtex, token ) &&
|
|
( rEXtex == NULL ||
|
|
!StrList_find( *rEXtex, token ) ) ){
|
|
texlist.emplace_back( token );
|
|
}
|
|
else{
|
|
Sys_FPrintf( SYS_WRN, "WARNING444: %s\n", token );
|
|
}
|
|
strcpy( dot, ".tga" ); // default extension for repacked shader text
|
|
}
|
|
|
|
|
|
/*
|
|
Append newcoming resource, if it is unique
|
|
*/
|
|
inline void res2list( StrList& list, const char* res ){
|
|
while ( path_separator( *res ) ){ // kill prepended slashes
|
|
++res;
|
|
}
|
|
if( !strEmpty( res ) && !StrList_find( list, res ) )
|
|
list.emplace_back( res );
|
|
}
|
|
|
|
|
|
static void parseBspFile( const char *bspPath, StrList& outShaders, StrList& outSounds, bool print ){
|
|
const char *str_p;
|
|
StringOutputStream stream( 256 );
|
|
StrList pk3Shaders;
|
|
StrList pk3Sounds;
|
|
|
|
|
|
/* load the bsp */
|
|
Sys_Printf( "Loading %s\n", bspPath );
|
|
LoadBSPFilePartially( bspPath );
|
|
ParseEntities();
|
|
|
|
{ /* add visible bspShaders */
|
|
std::vector<bool> drawsurfSHs( bspShaders.size(), false );
|
|
|
|
for ( const bspDrawSurface_t& surf : bspDrawSurfaces ){
|
|
drawsurfSHs[ surf.shaderNum ] = true;
|
|
}
|
|
|
|
for ( size_t i = 0; i < bspShaders.size(); ++i ){
|
|
if ( drawsurfSHs[i] && !( bspShaders[i].surfaceFlags & GetRequiredSurfaceParm( "nodraw"_Tstring ).surfaceFlags ) ){ // also sort out nodraw patches
|
|
res2list( pk3Shaders, bspShaders[i].shader );
|
|
}
|
|
}
|
|
}
|
|
/* Ent keys */
|
|
for ( const auto& ep : entities[0].epairs )
|
|
{
|
|
if ( striEqualPrefix( ep.key.c_str(), "vertexremapshader" ) ) {
|
|
char strbuf[ 1024 ];
|
|
if( 1 == sscanf( ep.value.c_str(), "%*[^;] %*[;] %s", strbuf ) ) // textures/remap/from;textures/remap/to
|
|
res2list( pk3Shaders, strbuf );
|
|
}
|
|
}
|
|
|
|
if ( entities[ 0 ].read_keyvalue( str_p, "music" ) ){
|
|
res2list( pk3Sounds, stream( PathDefaultExtension( PathCleaned( str_p ), ".wav" ) ) );
|
|
}
|
|
|
|
for ( const auto& e : entities )
|
|
{
|
|
if ( e.read_keyvalue( str_p, "noise" ) && str_p[0] != '*' ){
|
|
res2list( pk3Sounds, stream( PathDefaultExtension( PathCleaned( str_p ), ".wav" ) ) );
|
|
}
|
|
/* these 3 sound files are missing in vanilla bundle */
|
|
if ( e.classname_is( "func_plat" ) ){
|
|
res2list( pk3Sounds, "sound/movers/plats/pt1_strt.wav" );
|
|
res2list( pk3Sounds, "sound/movers/plats/pt1_end.wav" );
|
|
}
|
|
if ( e.classname_is( "target_push" ) ){
|
|
if ( !( e.intForKey( "spawnflags") & 1 ) ){
|
|
res2list( pk3Sounds, "sound/misc/windfly.wav" );
|
|
}
|
|
}
|
|
res2list( pk3Shaders, e.valueForKey( "targetShaderNewName" ) );
|
|
|
|
if ( e.read_keyvalue( str_p, "model2" ) ){
|
|
Sys_Warning( "unhandled model2 key of %s: %s\n", e.classname(), str_p );
|
|
}
|
|
}
|
|
|
|
for( const bspFog_t& fog : bspFogs ){
|
|
res2list( pk3Shaders, fog.shader );
|
|
}
|
|
|
|
//levelshot
|
|
res2list( pk3Shaders, stream( "levelshots/", PathFilename( bspPath ) ) );
|
|
|
|
|
|
if( print ){
|
|
Sys_Printf( "\n\tDrawsurface+ent calls....%zu\n", pk3Shaders.size() );
|
|
for ( const auto& s : pk3Shaders ){
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
}
|
|
Sys_Printf( "\n\tSounds....%zu\n", pk3Sounds.size() );
|
|
for ( const auto& s : pk3Sounds ){
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
}
|
|
}
|
|
|
|
/* merge to out lists */
|
|
for( const auto& s : pk3Shaders )
|
|
res2list( outShaders, s );
|
|
for( const auto& s : pk3Sounds )
|
|
res2list( outSounds, s );
|
|
}
|
|
|
|
|
|
|
|
struct Exclusions
|
|
{
|
|
StrList textures;
|
|
StrList shaders;
|
|
StrList shaderfiles;
|
|
StrList sounds;
|
|
StrList videos;
|
|
StrList pureTextures;
|
|
|
|
void print() const {
|
|
Sys_Printf( "\n\tExTextures....%zu\n", textures.size() );
|
|
for ( const auto& s : textures )
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
Sys_Printf( "\n\tExPureTextures....%zu\n", pureTextures.size() );
|
|
for ( const auto& s : pureTextures )
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
Sys_Printf( "\n\tExShaders....%zu\n", shaders.size() );
|
|
for ( const auto& s : shaders )
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
Sys_Printf( "\n\tExShaderfiles....%zu\n", shaderfiles.size() );
|
|
for ( const auto& s : shaderfiles )
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
Sys_Printf( "\n\tExSounds....%zu\n", sounds.size() );
|
|
for ( const auto& s : sounds )
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
Sys_Printf( "\n\tExVideos....%zu\n", videos.size() );
|
|
for ( const auto& s : videos )
|
|
Sys_Printf( "%s\n", s.c_str() );
|
|
|
|
}
|
|
};
|
|
|
|
static inline void parseEXblock( StrList& list, const char *exName ){
|
|
if ( !( GetToken( true ) && strEqual( token, "{" ) ) ) {
|
|
Error( "ReadExclusionsFile: %s, line %d: { not found", exName, scriptline );
|
|
}
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
if ( strEqual( token, "{" ) ) {
|
|
Error( "ReadExclusionsFile: %s, line %d: brace, opening twice in a row.", exName, scriptline );
|
|
}
|
|
|
|
/* add to list */
|
|
list.emplace_back( token );
|
|
}
|
|
return;
|
|
}
|
|
|
|
static const Exclusions parseEXfile( const char* filename ){
|
|
Exclusions ex;
|
|
if ( LoadScriptFile( filename, -1 ) ) {
|
|
/* tokenize it */
|
|
while ( GetToken( true ) ) /* test for end of file */
|
|
{
|
|
/* blocks */
|
|
if ( striEqual( token, "textures" ) ){
|
|
parseEXblock( ex.textures, filename );
|
|
}
|
|
else if ( striEqual( token, "shaders" ) ){
|
|
parseEXblock( ex.shaders, filename );
|
|
}
|
|
else if ( striEqual( token, "shaderfiles" ) ){
|
|
parseEXblock( ex.shaderfiles, filename );
|
|
}
|
|
else if ( striEqual( token, "sounds" ) ){
|
|
parseEXblock( ex.sounds, filename );
|
|
}
|
|
else if ( striEqual( token, "videos" ) ){
|
|
parseEXblock( ex.videos, filename );
|
|
}
|
|
else{
|
|
Error( "ReadExclusionsFile: %s, line %d: unknown block name!\nValid ones are: textures, shaders, shaderfiles, sounds, videos.", filename, scriptline );
|
|
}
|
|
}
|
|
|
|
/* prepare ex.pureTextures */
|
|
for ( const auto& s : ex.textures ){
|
|
if( !StrList_find( ex.shaders, s ) )
|
|
ex.pureTextures.emplace_back( s );
|
|
}
|
|
}
|
|
else{
|
|
Sys_Warning( "Unable to load exclusions file %s.\n", filename );
|
|
}
|
|
|
|
return ex;
|
|
}
|
|
|
|
|
|
|
|
static bool packResource( const char* resname, const char* packname, const int compLevel ){
|
|
const bool ret = vfsPackFile( resname, packname, compLevel );
|
|
if ( ret )
|
|
Sys_Printf( "++%s\n", resname );
|
|
return ret;
|
|
}
|
|
static bool packTexture( const char* texname, const char* packname, const int compLevel, const bool png ){
|
|
const char* extensions[4] = { ".png", ".tga", ".jpg", 0 };
|
|
for ( const char** ext = extensions + !png; *ext; ++ext ){
|
|
char str[MAX_QPATH * 2];
|
|
sprintf( str, "%s%s", texname, *ext );
|
|
if( packResource( str, packname, compLevel ) ){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
pk3BSPMain()
|
|
map autopackager, works for Q3 type of shaders and ents
|
|
*/
|
|
|
|
int pk3BSPMain( Args& args ){
|
|
int compLevel = 9; // MZ_BEST_COMPRESSION; MZ_UBER_COMPRESSION : not zlib compatible, and may be very slow
|
|
bool dbg = false, png = false, packFAIL = false;
|
|
StringOutputStream stream( 256 );
|
|
|
|
/* process arguments */
|
|
const char *fileName = args.takeBack();
|
|
{
|
|
if ( args.takeArg( "-dbg" ) ) {
|
|
dbg = true;
|
|
}
|
|
if ( args.takeArg( "-png" ) ) {
|
|
png = true;
|
|
}
|
|
if ( args.takeArg( "-complevel" ) ) {
|
|
compLevel = std::clamp( atoi( args.takeNext() ), -1, 10 );
|
|
Sys_Printf( "Compression level set to %i\n", compLevel );
|
|
}
|
|
}
|
|
|
|
/* extract pack name */
|
|
const CopiedString nameOFpack( PathFilename( fileName ) );
|
|
|
|
std::vector<CopiedString> bspList; // absolute bsp paths
|
|
|
|
while( !args.empty() ){ // handle multiple bsps input
|
|
bspList.emplace_back( stream( PathExtensionless( ExpandArg( args.takeFront() ) ), ".bsp" ) );
|
|
}
|
|
bspList.emplace_back( stream( PathExtensionless( ExpandArg( fileName ) ), ".bsp" ) );
|
|
|
|
/* parse bsps */
|
|
StrList pk3Shaders;
|
|
StrList pk3Sounds;
|
|
std::vector<CopiedString> pk3Shaderfiles;
|
|
StrList pk3Textures;
|
|
StrList pk3Videos;
|
|
|
|
for( const auto& bsp : bspList ){
|
|
parseBspFile( bsp.c_str(), pk3Shaders, pk3Sounds, dbg );
|
|
}
|
|
|
|
pk3Shaderfiles = vfsListShaderFiles( g_game->shaderPath );
|
|
|
|
if( dbg ){
|
|
Sys_Printf( "\n\tSchroider fileses.....%zu\n", pk3Shaderfiles.size() );
|
|
for ( const CopiedString& file : pk3Shaderfiles ){
|
|
Sys_Printf( "%s\n", file.c_str() );
|
|
}
|
|
}
|
|
|
|
/* load exclusions file */
|
|
const Exclusions ex = parseEXfile( stream( PathFilenameless( args.getArg0() ), g_game->arg, ".exclude" ) );
|
|
/* key is pointer to pk3Shaders entry, thus latter shall not be reallocated! */
|
|
std::map<const String64*, CopiedString> ExReasonShader;
|
|
std::map<const String64*, CopiedString> ExReasonShaderFile;
|
|
|
|
if( dbg )
|
|
ex.print();
|
|
|
|
/* can exclude pure textures right now, shouldn't create shaders for them anyway */
|
|
for ( auto& s : pk3Shaders ){
|
|
if( StrList_find( ex.pureTextures, s ) )
|
|
s.clear();
|
|
}
|
|
|
|
//Parse Shader Files
|
|
for ( CopiedString& file : pk3Shaderfiles ){
|
|
bool wantShaderFile = false;
|
|
const String64 *excludedByShader = nullptr;
|
|
/* do wanna le shader file? */
|
|
const bool excludedByShaderFile = StrList_find( ex.shaderfiles, file.c_str() );
|
|
|
|
/* load the shader */
|
|
const auto scriptFile = stream( g_game->shaderPath, '/', file );
|
|
|
|
/* check if shader file has to be excluded */
|
|
if( !excludedByShaderFile ){
|
|
/* tokenize it */
|
|
LoadScriptFile( scriptFile, 0, false );
|
|
while ( GetToken( true ) )
|
|
{
|
|
/* does it contain restricted shaders/textures? */
|
|
if( ( excludedByShader = StrList_find( ex.shaders, token ) )
|
|
|| ( excludedByShader = StrList_find( ex.pureTextures, token ) ) ){
|
|
break;
|
|
}
|
|
|
|
/* handle { } section */
|
|
if ( !( GetToken( true ) && strEqual( token, "{" ) ) ) {
|
|
Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
|
|
scriptFile.c_str(), scriptline, token, g_strLoadedFileLocation );
|
|
}
|
|
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
/* parse stage directives */
|
|
if ( strEqual( token, "{" ) ) {
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* tokenize it again */
|
|
LoadScriptFile( scriptFile, 0, dbg );
|
|
while ( GetToken( true ) )
|
|
{
|
|
//dump shader names
|
|
if( dbg )
|
|
Sys_Printf( "%s\n", token );
|
|
|
|
/* do wanna le shader? */
|
|
String64 *wantShader = StrList_find( pk3Shaders, token );
|
|
|
|
/* handle { } section */
|
|
if ( !( GetToken( true ) && strEqual( token, "{" ) ) ) {
|
|
Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
|
|
scriptFile.c_str(), scriptline, token, g_strLoadedFileLocation );
|
|
}
|
|
|
|
bool hasmap = false;
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
|
|
/* -----------------------------------------------------------------
|
|
shader stages (passes)
|
|
----------------------------------------------------------------- */
|
|
|
|
/* parse stage directives */
|
|
if ( strEqual( token, "{" ) ) {
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
if ( strEqual( token, "{" ) ) {
|
|
Sys_FPrintf( SYS_WRN, "WARNING9: %s : line %d : opening brace inside shader stage\n", scriptFile.c_str(), scriptline );
|
|
}
|
|
if ( striEqual( token, "mapComp" ) || striEqual( token, "mapNoComp" ) || striEqual( token, "animmapcomp" ) || striEqual( token, "animmapnocomp" ) ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING7: %s : line %d : unsupported '%s' map directive\n", scriptFile.c_str(), scriptline, token );
|
|
}
|
|
/* skip the shader */
|
|
if ( !wantShader )
|
|
continue;
|
|
|
|
/* digest any images */
|
|
if ( striEqual( token, "map" ) ||
|
|
striEqual( token, "clampMap" ) ) {
|
|
hasmap = true;
|
|
/* get an image */
|
|
GetToken( false );
|
|
if ( token[ 0 ] != '*' && token[ 0 ] != '$' ) {
|
|
tex2list( pk3Textures, ex.textures, NULL );
|
|
}
|
|
}
|
|
else if ( striEqual( token, "animMap" ) ||
|
|
striEqual( token, "clampAnimMap" ) ) {
|
|
hasmap = true;
|
|
GetToken( false );// skip num
|
|
while ( TokenAvailable() ){
|
|
GetToken( false );
|
|
tex2list( pk3Textures, ex.textures, NULL );
|
|
}
|
|
}
|
|
else if ( striEqual( token, "videoMap" ) ){
|
|
hasmap = true;
|
|
GetToken( false );
|
|
FixDOSName( token );
|
|
if ( strchr( token, '/' ) == NULL ){
|
|
strcpy( token, stream( "video/", token ) );
|
|
}
|
|
if( !StrList_find( pk3Videos, token ) &&
|
|
!StrList_find( ex.videos, token ) )
|
|
pk3Videos.emplace_back( token );
|
|
}
|
|
}
|
|
}
|
|
else if ( striEqualPrefix( token, "implicit" ) ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING5: %s : line %d : unsupported %s shader\n", scriptFile.c_str(), scriptline, token );
|
|
}
|
|
/* skip the shader */
|
|
else if ( !wantShader )
|
|
continue;
|
|
|
|
/* skyparms <outer image> <cloud height> <inner image> */
|
|
else if ( striEqual( token, "skyParms" ) ) {
|
|
hasmap = true;
|
|
/* get image base */
|
|
GetToken( false );
|
|
|
|
/* ignore bogus paths */
|
|
if ( !strEqual( token, "-" ) && !striEqual( token, "full" ) ) {
|
|
char* const skysidestring = token + strcatQ( token, "_@@.tga", std::size( token ) ) - 6;
|
|
for( const auto side : { "up", "dn", "lf", "rt", "bk", "ft" } ){
|
|
memcpy( skysidestring, side, 2 );
|
|
tex2list( pk3Textures, ex.textures, NULL );
|
|
}
|
|
}
|
|
/* skip rest of line */
|
|
GetToken( false );
|
|
GetToken( false );
|
|
}
|
|
else if ( striEqual( token, "fogparms" ) ){
|
|
hasmap = true;
|
|
}
|
|
}
|
|
|
|
//exclude shader
|
|
if ( wantShader ){
|
|
if( StrList_find( ex.shaders, *wantShader ) ){
|
|
wantShader->clear();
|
|
wantShader = nullptr;
|
|
}
|
|
if ( wantShader && !hasmap ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING8: %s : visible shader has no known maps\n", wantShader->c_str() );
|
|
wantShader = nullptr;
|
|
}
|
|
if ( wantShader ){
|
|
if ( excludedByShader || excludedByShaderFile ){
|
|
ExReasonShaderFile[ wantShader ] = file;
|
|
if( excludedByShader != nullptr ){
|
|
ExReasonShader[ wantShader ] = excludedByShader->c_str();
|
|
}
|
|
}
|
|
else{
|
|
wantShaderFile = true;
|
|
wantShader->clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( !wantShaderFile ){
|
|
file = "";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* exclude stuff */
|
|
//wanted shaders from excluded .shaders
|
|
Sys_Printf( "\n" );
|
|
for ( auto& s : pk3Shaders ){
|
|
if ( !s.empty() && ( ExReasonShader.count( &s ) || ExReasonShaderFile.count( &s ) ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
packFAIL = true;
|
|
if ( ExReasonShader.count( &s ) ){
|
|
Sys_Printf( " reason: is located in %s,\n containing restricted shader %s\n", ExReasonShaderFile[&s].c_str(), ExReasonShader[&s].c_str() );
|
|
}
|
|
else{
|
|
Sys_Printf( " reason: is located in restricted %s\n", ExReasonShaderFile[&s].c_str() );
|
|
}
|
|
s.clear();
|
|
}
|
|
}
|
|
//pure textures (shader ones are done)
|
|
for ( auto& s : pk3Shaders ){
|
|
if ( !s.empty() ){
|
|
s = stream( PathCleaned( s ) );
|
|
if( StrList_find( pk3Textures, s ) ||
|
|
StrList_find( ex.textures, s ) )
|
|
s.clear();
|
|
}
|
|
}
|
|
|
|
//snds
|
|
for ( auto& s : pk3Sounds ){
|
|
if( StrList_find( ex.sounds, s ) )
|
|
s.clear();
|
|
}
|
|
|
|
/* make a pack */
|
|
const auto packname = stream( g_enginePath, nameOFpack, "_autopacked.pk3" );
|
|
remove( packname );
|
|
const auto packFailName = stream( g_enginePath, nameOFpack, "_FAILEDpack.pk3" );
|
|
remove( packFailName );
|
|
|
|
Sys_Printf( "\n--- ZipZip ---\n" );
|
|
|
|
Sys_Printf( "\n\tShader referenced textures....\n" );
|
|
|
|
for ( const auto& s : pk3Textures ){
|
|
if( !packTexture( s, packname, compLevel, png ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
packFAIL = true;
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tPure textures....\n" );
|
|
|
|
for ( const auto& s : pk3Shaders ){
|
|
if ( !s.empty() ){
|
|
if( !packTexture( s, packname, compLevel, png ) ){
|
|
if ( &s == &pk3Shaders.back() ){ //levelshot typically
|
|
Sys_Printf( " ~fail %s\n", s.c_str() );
|
|
}
|
|
else{
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
packFAIL = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tShaizers....\n" );
|
|
|
|
for ( CopiedString& file : pk3Shaderfiles ){
|
|
if ( !file.empty() ){
|
|
stream( g_game->shaderPath, "/", file.c_str() );
|
|
if ( !packResource( stream, packname, compLevel ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", stream.c_str() );
|
|
packFAIL = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tSounds....\n" );
|
|
|
|
for ( const auto& s : pk3Sounds ){
|
|
if ( !s.empty() ){
|
|
if ( !packResource( s, packname, compLevel ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
packFAIL = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tVideos....\n" );
|
|
|
|
for ( const auto& s : pk3Videos ){
|
|
if ( !packResource( s, packname, compLevel ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
packFAIL = true;
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\t.bsp and stuff\n" );
|
|
|
|
for( const auto& bsp : bspList ){
|
|
const auto mapname = PathFilename( bsp.c_str() );
|
|
stream( "maps/", mapname, ".bsp" );
|
|
//if ( vfsPackFile( stream, packname, compLevel ) ){
|
|
if ( vfsPackFile_Absolute_Path( bsp.c_str(), stream, packname, compLevel ) ){
|
|
Sys_Printf( "++%s\n", stream.c_str() );
|
|
}
|
|
else{
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", stream.c_str() );
|
|
packFAIL = true;
|
|
}
|
|
|
|
stream( "maps/", mapname, ".aas" );
|
|
if ( !packResource( stream, packname, compLevel ) )
|
|
Sys_Printf( " ~fail %s\n", stream.c_str() );
|
|
|
|
stream( "scripts/", mapname, ".arena" );
|
|
if ( !packResource( stream, packname, compLevel ) )
|
|
Sys_Printf( " ~fail %s\n", stream.c_str() );
|
|
|
|
stream( "scripts/", mapname, ".defi" );
|
|
if ( !packResource( stream, packname, compLevel ) )
|
|
Sys_Printf( " ~fail %s\n", stream.c_str() );
|
|
}
|
|
|
|
if ( !packFAIL ){
|
|
Sys_Printf( "\nSaved to %s\n", packname.c_str() );
|
|
}
|
|
else{
|
|
rename( packname, packFailName );
|
|
Sys_Printf( "\nSaved to %s\n", packFailName.c_str() );
|
|
}
|
|
/* return to sender */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void findShaderDups(){
|
|
LoadShaderInfo();
|
|
struct ShaderMaps
|
|
{
|
|
String64 box;
|
|
StrList maps;
|
|
const shaderInfo_t *si;
|
|
|
|
bool equal_box( const ShaderMaps& other ) const {
|
|
return striEqual( box, other.box );
|
|
}
|
|
bool equal_maps( const ShaderMaps& other ) const {
|
|
if( maps.size() != other.maps.size() )
|
|
return false;
|
|
for( size_t i = 0; i < maps.size(); ++i ){
|
|
if( !striEqual( maps[i], other.maps[i] ) ){
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
std::vector<ShaderMaps> shaderMapsList;
|
|
|
|
for( const auto& si : Span( shaderInfo, shaderInfo + numShaderInfo ) ){
|
|
/* load the shader */
|
|
ParseFromMemory( si.shaderText, strlen( si.shaderText ) + 1 );
|
|
|
|
/* tokenize it */
|
|
{
|
|
/* handle { } section */
|
|
if ( !( GetToken( true ) && strEqual( token, "{" ) ) ) {
|
|
Error( "ParseShader: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
|
|
si.shader.c_str(), scriptline, token, g_strLoadedFileLocation );
|
|
}
|
|
|
|
bool hasmap = false;
|
|
ShaderMaps& shaderMaps = shaderMapsList.emplace_back();
|
|
shaderMaps.si = &si;
|
|
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
/* parse stage directives */
|
|
if ( strEqual( token, "{" ) ) {
|
|
while ( GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
/* digest any images */
|
|
if ( striEqual( token, "map" ) ||
|
|
striEqual( token, "clampMap" ) ) {
|
|
hasmap = true;
|
|
|
|
/* get an image */
|
|
GetToken( false );
|
|
if ( token[ 0 ] != '*' && token[ 0 ] != '$' ) {
|
|
shaderMaps.maps.emplace_back( token );
|
|
}
|
|
}
|
|
else if ( striEqual( token, "animMap" ) ||
|
|
striEqual( token, "clampAnimMap" ) ) {
|
|
hasmap = true;
|
|
|
|
GetToken( false );// skip num
|
|
while ( TokenAvailable() ){
|
|
shaderMaps.maps.emplace_back( token );
|
|
GetToken( false ); // append token, modified by tex2list()
|
|
}
|
|
}
|
|
else if ( striEqual( token, "videoMap" ) ){
|
|
hasmap = true;
|
|
GetToken( false );
|
|
FixDOSName( token );
|
|
shaderMaps.maps.emplace_back( token );
|
|
}
|
|
else if ( striEqual( token, "mapComp" ) || striEqual( token, "mapNoComp" ) || striEqual( token, "animmapcomp" ) || striEqual( token, "animmapnocomp" ) ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING7: %s : %s shader\n", si.shader.c_str(), token );
|
|
hasmap = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* skyparms <outer image> <cloud height> <inner image> */
|
|
else if ( striEqual( token, "skyParms" ) ) {
|
|
hasmap = true;
|
|
/* get image base */
|
|
GetToken( false );
|
|
|
|
/* ignore bogus paths */
|
|
if ( !strEqual( token, "-" ) && !striEqual( token, "full" ) ) {
|
|
shaderMaps.box = token;
|
|
}
|
|
/* skip rest of line */
|
|
GetToken( false );
|
|
GetToken( false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StringOutputStream outa, outb;
|
|
|
|
const auto save = [&outa, &outb]( const char *suffix ){
|
|
StringOutputStream filename( 256 );
|
|
filename( g_enginePath, "shadersDiff", suffix, "A.shader" );
|
|
SaveFile( filename, outa.c_str(), outa.end() - outa.begin() );
|
|
Sys_Printf( "\nSaved to %s\n", filename.c_str() );
|
|
filename( g_enginePath, "shadersDiff", suffix, "B.shader" );
|
|
SaveFile( filename, outb.c_str(), outb.end() - outb.begin() );
|
|
Sys_Printf( "\nSaved to %s\n", filename.c_str() );
|
|
outa.clear();
|
|
outb.clear();
|
|
};
|
|
|
|
for( auto a = shaderMapsList.cbegin(); a != shaderMapsList.cend(); ++a )
|
|
{
|
|
for( auto b = a + 1; b != shaderMapsList.cend(); ++b )
|
|
{
|
|
if( a->equal_box( *b ) && a->equal_maps( *b ) ){
|
|
outa << "\n//========================================\n";
|
|
outb << "\n//========================================\n";
|
|
outa << a->si->shader << a->si->shaderText << '\n';
|
|
outb << b->si->shader << b->si->shaderText << '\n';
|
|
}
|
|
}
|
|
}
|
|
save( "" );
|
|
#if 1
|
|
for( auto a = shaderMapsList.cbegin(); a != shaderMapsList.cend(); ++a )
|
|
{
|
|
for( auto b = a + 1; b != shaderMapsList.cend(); ++b )
|
|
{
|
|
if( !a->maps.empty() && a->equal_maps( *b ) ){
|
|
outa << "\n//========================================@@@@@@@@===EQ_CLOUDS\n";
|
|
outb << "\n//========================================@@@@@@@@===EQ_CLOUDS\n";
|
|
outa << a->si->shader << a->si->shaderText << '\n';
|
|
outb << b->si->shader << b->si->shaderText << '\n';
|
|
}
|
|
}
|
|
}
|
|
save( "_EQ_CLOUDS_" );
|
|
|
|
std::sort( shaderMapsList.begin(), shaderMapsList.end(),
|
|
[]( const ShaderMaps& a, const ShaderMaps& b ){
|
|
return ( string_compare_nocase( a.box, b.box ) < 0 );
|
|
} );
|
|
for( auto a = shaderMapsList.cbegin(); a != shaderMapsList.cend(); ++a )
|
|
{
|
|
for( auto b = a + 1; b != shaderMapsList.cend(); ++b )
|
|
{
|
|
if( !a->box.empty() && a->equal_box( *b ) ){
|
|
outa << "\n//========================================######===EQ_BOX\n";
|
|
outb << "\n//========================================######===EQ_BOX\n";
|
|
outa << a->si->shader << a->si->shaderText << '\n';
|
|
outb << b->si->shader << b->si->shaderText << '\n';
|
|
}
|
|
}
|
|
}
|
|
save( "_EQ_BOX_" );
|
|
|
|
for( auto a = shaderMapsList.cbegin(); a != shaderMapsList.cend(); ++a )
|
|
{
|
|
for( auto b = a + 1; b != shaderMapsList.cend(); ++b )
|
|
{
|
|
if( !a->equal_maps( *b ) ){
|
|
for( const auto& amap : a->maps )
|
|
for( const auto& bmap : b->maps )
|
|
if( striEqual( amap, bmap ) ){
|
|
outa << "\n//===========@@@@@@@@===EQ_MAP: " << amap << '\n';
|
|
outb << "\n//===========@@@@@@@@===EQ_MAP: " << bmap << '\n';
|
|
outa << a->si->shader << a->si->shaderText << '\n';
|
|
outb << b->si->shader << b->si->shaderText << '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
save( "_EQ_MAP_" );
|
|
#endif
|
|
std::sort( shaderInfo, shaderInfo + numShaderInfo,
|
|
[]( const shaderInfo_t& a, const shaderInfo_t& b ){
|
|
return ( string_compare_nocase( a.shader, b.shader ) < 0 );
|
|
} );
|
|
for( auto si = shaderInfo; si != shaderInfo + numShaderInfo - 1; ++si )
|
|
if( striEqual( si->shader, (si + 1)->shader ) ){
|
|
outa << "\n//========================================$$$===EQ_NAME\n";
|
|
outb << "\n//========================================$$$===EQ_NAME\n";
|
|
outa << si->shader << si->shaderText << '\n';
|
|
outb << (si + 1)->shader << (si + 1)->shaderText << '\n';
|
|
}
|
|
save( "_EQ_NAME_" );
|
|
}
|
|
|
|
// checks shader resources presence
|
|
class ShaderValidator
|
|
{
|
|
private:
|
|
const bool m_doThings;
|
|
const StrList& m_excludedTexs;
|
|
bool m_usesBase = false;
|
|
bool m_usesNotBase = false;
|
|
bool m_isPartiallyValid = false; //has some present res
|
|
bool m_isInvalid = false; //has some missing res
|
|
StrList m_missing;
|
|
public:
|
|
ShaderValidator( bool doThings, const StrList& excludedTexs ) : m_doThings( doThings ), m_excludedTexs( excludedTexs ){
|
|
}
|
|
void test( const char *intex ){
|
|
if( m_doThings ){
|
|
const auto tex = String64()( PathExtensionless( intex ) );
|
|
if( StrList_find( m_excludedTexs, tex ) ){
|
|
m_usesBase = true;
|
|
m_isPartiallyValid = true;
|
|
}
|
|
else{
|
|
m_usesNotBase = true;
|
|
bool found = false;
|
|
for( const auto ext : { ".png", ".tga", ".jpg" } )
|
|
{
|
|
if( vfsGetFileCount( String64()( tex, ext ) ) ){
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
m_isPartiallyValid |= found;
|
|
m_isInvalid |= !found;
|
|
if( !found )
|
|
m_missing.emplace_back( intex );
|
|
}
|
|
}
|
|
}
|
|
bool valid() const {
|
|
return !m_doThings || ( m_isPartiallyValid );
|
|
}
|
|
const StringOutputStream getComment() const {
|
|
StringOutputStream str;
|
|
if( m_doThings ){
|
|
if( m_isInvalid ){
|
|
for( const auto& miss : m_missing )
|
|
str << "\n//! m_isInvalid " << miss;
|
|
}
|
|
if( m_usesBase )
|
|
if( !m_usesNotBase )
|
|
str << "\n//! m_isPureBase";
|
|
else
|
|
str << "\n//! m_usesBase";
|
|
}
|
|
return str;
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
repackBSPMain()
|
|
repack multiple maps, strip out only required shaders
|
|
works for Q3 type of shaders and ents
|
|
*/
|
|
|
|
int repackBSPMain( Args& args ){
|
|
int compLevel = 1; // MZ_BEST_SPEED
|
|
bool dbg = false, png = false, analyze = false;
|
|
StringOutputStream stream( 256 );
|
|
/* process arguments */
|
|
const char *fileName = args.takeBack();
|
|
{
|
|
if ( args.takeArg( "-dbg" ) ) {
|
|
dbg = true;
|
|
}
|
|
if ( args.takeArg( "-png" ) ) {
|
|
png = true;
|
|
}
|
|
if ( args.takeArg( "-analyze" ) ) { // only analyze bsps and exit
|
|
analyze = true;
|
|
}
|
|
if ( args.takeArg( "-complevel" ) ) {
|
|
compLevel = std::clamp( atoi( args.takeNext() ), -1, 10 );
|
|
Sys_Printf( "Compression level set to %i\n", compLevel );
|
|
}
|
|
}
|
|
const bool validateShaders = args.takeArg( "-validateShaders" );
|
|
const bool packDups = args.takeArg( "-packDups" );
|
|
const bool packQer = args.takeArg( "-packQer" );
|
|
|
|
if( args.takeArg( "-shaderDups" ) ){
|
|
findShaderDups();
|
|
return 0;
|
|
}
|
|
|
|
/* extract pack name */
|
|
const CopiedString nameOFpack( PathFilename( fileName ) );
|
|
|
|
/* load exclusions file */
|
|
const Exclusions ex = parseEXfile( stream( PathFilenameless( args.getArg0() ), g_game->arg, ".exclude" ) );
|
|
|
|
if( dbg )
|
|
ex.print();
|
|
|
|
/* load repack.exclude */ /* rex.pureTextures & rex.shaderfiles wont be used */
|
|
const Exclusions rex = parseEXfile( stream( PathFilenameless( args.getArg0() ), "repack.exclude" ) );
|
|
|
|
if( dbg )
|
|
rex.print();
|
|
|
|
|
|
std::vector<CopiedString> bspList; // absolute bsp paths
|
|
|
|
if ( path_extension_is( fileName, "bsp" ) ){
|
|
while( !args.empty() ){ // handle multiple bsps input
|
|
bspList.emplace_back( stream( PathExtensionless( ExpandArg( args.takeFront() ) ), ".bsp" ) );
|
|
}
|
|
bspList.emplace_back( stream( PathExtensionless( ExpandArg( fileName ) ), ".bsp" ) );
|
|
}
|
|
else{
|
|
/* load bsps paths list */
|
|
/* do some path mangling */
|
|
strcpy( source, ExpandArg( fileName ) );
|
|
if ( !LoadScriptFile( source, -1 ) ) {
|
|
Error( "Unable to open bsps paths list file %s.\n", source );
|
|
}
|
|
|
|
/* tokenize it */
|
|
while ( GetToken( true ) )
|
|
{
|
|
bspList.emplace_back( stream( PathExtensionless( token ), ".bsp" ) );
|
|
}
|
|
}
|
|
|
|
/* parse bsps */
|
|
StrList pk3Shaders;
|
|
StrList pk3Sounds;
|
|
std::vector<CopiedString> pk3Shaderfiles;
|
|
StrList pk3Textures;
|
|
StrList pk3Videos;
|
|
|
|
|
|
for( const auto& bsp : bspList ){
|
|
parseBspFile( stream( PathExtensionless( bsp.c_str() ), ".bsp" ), pk3Shaders, pk3Sounds, true );
|
|
}
|
|
|
|
if( analyze )
|
|
return 0;
|
|
|
|
LoadShaderInfo();
|
|
for( const auto& si : Span( shaderInfo, shaderInfo + numShaderInfo ) ){
|
|
if( strIstr( si.shaderText, "skyparms" ) ){
|
|
res2list( pk3Shaders, si.shader );
|
|
}
|
|
}
|
|
|
|
std::sort( shaderInfo, shaderInfo + numShaderInfo,
|
|
[]( const shaderInfo_t& a, const shaderInfo_t& b ){
|
|
if( string_compare_nocase( a.shader, b.shader ) < 0 )
|
|
return true;
|
|
else if( string_compare_nocase( a.shader, b.shader ) > 0 )
|
|
return false;
|
|
else if( string_compare_nocase( a.skyParmsImageBase, b.skyParmsImageBase ) < 0 )
|
|
return true;
|
|
else
|
|
return false;
|
|
} );
|
|
for( auto si = shaderInfo; si != shaderInfo + numShaderInfo - 1; ++si )
|
|
if( striEqual( si->shader, (si + 1)->shader ) && !striEqual( si->skyParmsImageBase, (si + 1)->skyParmsImageBase ) )
|
|
Sys_Printf( "WARNING666: %s : %s | %s\n", si->shader.c_str(), si->skyParmsImageBase.c_str(), (si + 1)->skyParmsImageBase.c_str() );
|
|
|
|
|
|
pk3Shaderfiles = vfsListShaderFiles( g_game->shaderPath );
|
|
|
|
if( dbg ){
|
|
Sys_Printf( "\n\tSchroider fileses.....%zu\n", pk3Shaderfiles.size() );
|
|
for ( const CopiedString& file : pk3Shaderfiles ){
|
|
Sys_Printf( "%s\n", file.c_str() );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* can exclude pure *base* textures right now, shouldn't create shaders for them anyway */
|
|
for ( auto& s : pk3Shaders ){
|
|
if( StrList_find( ex.pureTextures, s ) )
|
|
s.clear();
|
|
}
|
|
/* can exclude repack.exclude shaders, assuming they got all their images */
|
|
for ( auto& s : pk3Shaders ){
|
|
if( StrList_find( rex.shaders, s ) )
|
|
s.clear();
|
|
}
|
|
|
|
//Parse Shader Files
|
|
Sys_Printf( "\t\nParsing shaders....\n\n" );
|
|
ShaderTextCollector text;
|
|
StringOutputStream allShaders( 1048576 );
|
|
|
|
for ( const CopiedString& file : pk3Shaderfiles ){
|
|
/* load the shader */
|
|
const auto scriptFile = stream( g_game->shaderPath, '/', file );
|
|
LoadScriptFile( scriptFile, 0, dbg );
|
|
|
|
/* tokenize it */
|
|
while ( text.GetToken( true ) ) /* test for end of file */
|
|
{
|
|
//dump shader names
|
|
if( dbg )
|
|
Sys_Printf( "%s\n", token );
|
|
|
|
if ( strchr( token, '\\') != NULL ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING1: %s : %s : shader name with backslash\n", file.c_str(), token );
|
|
}
|
|
|
|
/* do wanna le shader? */
|
|
String64 *wantShader = StrList_find( pk3Shaders, token );
|
|
|
|
ShaderValidator shaderValidator( validateShaders, ex.textures );
|
|
|
|
/* handle { } section */
|
|
if ( !( text.GetToken( true ) && strEqual( token, "{" ) ) ) {
|
|
Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
|
|
scriptFile.c_str(), scriptline, token, g_strLoadedFileLocation );
|
|
}
|
|
|
|
bool hasmap = false;
|
|
|
|
while ( text.GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
/* parse stage directives */
|
|
if ( strEqual( token, "{" ) ) {
|
|
while ( text.GetToken( true ) && !strEqual( token, "}" ) )
|
|
{
|
|
/* skip the shader */
|
|
if ( !wantShader )
|
|
continue;
|
|
|
|
/* digest any images */
|
|
if ( striEqual( token, "map" ) ||
|
|
striEqual( token, "clampMap" ) ) {
|
|
hasmap = true;
|
|
|
|
/* get an image */
|
|
GetToken( false );
|
|
if ( token[ 0 ] != '*' && token[ 0 ] != '$' ) {
|
|
tex2list( pk3Textures, ex.textures, &rex.textures );
|
|
shaderValidator.test( token );
|
|
}
|
|
text.tokenAppend(); // append token, modified by tex2list()
|
|
}
|
|
else if ( striEqual( token, "animMap" ) ||
|
|
striEqual( token, "clampAnimMap" ) ) {
|
|
hasmap = true;
|
|
|
|
text.GetToken( false );// skip num
|
|
while ( TokenAvailable() ){
|
|
tex2list( pk3Textures, ex.textures, &rex.textures );
|
|
shaderValidator.test( token );
|
|
text.GetToken( false ); // append token, modified by tex2list()
|
|
}
|
|
}
|
|
else if ( striEqual( token, "videoMap" ) ){
|
|
hasmap = true;
|
|
text.GetToken( false );
|
|
FixDOSName( token );
|
|
if ( strchr( token, '/' ) == NULL ){
|
|
strcpy( token, stream( "video/", token ) );
|
|
}
|
|
if( !StrList_find( pk3Videos, token ) &&
|
|
!StrList_find( ex.videos, token ) &&
|
|
!StrList_find( rex.videos, token ) )
|
|
pk3Videos.emplace_back( token );
|
|
}
|
|
else if ( striEqual( token, "mapComp" ) || striEqual( token, "mapNoComp" ) || striEqual( token, "animmapcomp" ) || striEqual( token, "animmapnocomp" ) ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING7: %s : %s shader\n", wantShader->c_str(), token );
|
|
hasmap = true;
|
|
}
|
|
}
|
|
}
|
|
/* skip the shader */
|
|
else if ( !wantShader )
|
|
continue;
|
|
|
|
/* skyparms <outer image> <cloud height> <inner image> */
|
|
else if ( striEqual( token, "skyParms" ) ) {
|
|
hasmap = true;
|
|
/* get image base */
|
|
text.GetToken( false );
|
|
|
|
/* ignore bogus paths */
|
|
if ( !strEqual( token, "-" ) && !striEqual( token, "full" ) ) {
|
|
char* const skysidestring = token + strcatQ( token, "_@@.tga", std::size( token ) ) - 6;
|
|
for( const auto side : { "up", "dn", "lf", "rt", "bk", "ft" } ){
|
|
memcpy( skysidestring, side, 2 );
|
|
tex2list( pk3Textures, ex.textures, &rex.textures );
|
|
shaderValidator.test( token );
|
|
}
|
|
}
|
|
/* skip rest of line */
|
|
text.GetToken( false );
|
|
text.GetToken( false );
|
|
}
|
|
else if ( packQer && striEqual( token, "qer_editorimage" ) ) {
|
|
/* get an image */
|
|
GetToken( false );
|
|
tex2list( pk3Textures, ex.textures, &rex.textures );
|
|
shaderValidator.test( token );
|
|
text.tokenAppend(); // append token, modified by tex2list()
|
|
}
|
|
else if ( striEqualPrefix( token, "implicit" ) ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING5: %s : %s shader\n", wantShader->c_str(), token );
|
|
hasmap = true;
|
|
}
|
|
else if ( striEqual( token, "fogparms" ) ){
|
|
hasmap = true;
|
|
}
|
|
}
|
|
|
|
//exclude shader
|
|
if ( wantShader ){
|
|
if( StrList_find( ex.shaders, *wantShader ) ){
|
|
wantShader->clear();
|
|
wantShader = nullptr;
|
|
}
|
|
if( wantShader && StrList_find( rex.textures, *wantShader ) )
|
|
Sys_FPrintf( SYS_WRN, "WARNING3: %s : about to include shader for excluded texture\n", wantShader->c_str() );
|
|
if ( wantShader && !hasmap ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING8: %s : visible shader has no known maps\n", wantShader->c_str() );
|
|
wantShader = nullptr;
|
|
}
|
|
if ( wantShader ){
|
|
if( shaderValidator.valid() )
|
|
allShaders << shaderValidator.getComment() << text.text << '\n';
|
|
wantShader->clear();
|
|
}
|
|
}
|
|
/* reset collector */
|
|
text.clear();
|
|
}
|
|
}
|
|
/* TODO: RTCW's mapComp, mapNoComp, animmapcomp, animmapnocomp; nocompress?; ET's implicitmap, implicitblend, implicitmask */
|
|
|
|
|
|
/* exclude stuff */
|
|
|
|
//pure textures (shader ones are done)
|
|
for ( auto& s : pk3Shaders ){
|
|
if ( !s.empty() ){
|
|
if ( strchr( s, '\\') != NULL ){
|
|
Sys_FPrintf( SYS_WRN, "WARNING2: %s : bsp shader path with backslash\n", s.c_str() );
|
|
s = stream( PathCleaned( s ) );
|
|
//what if theres properly slashed one in the list?
|
|
for ( const auto& ss : pk3Shaders ){
|
|
if ( striEqual( s, ss ) && ( &s != &ss ) ){
|
|
s.clear();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( !s.empty() ){
|
|
if( StrList_find( pk3Textures, s ) ||
|
|
StrList_find( ex.textures, s ) ||
|
|
StrList_find( rex.textures, s ) )
|
|
s.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
//snds
|
|
for ( auto& s : pk3Sounds ){
|
|
if( StrList_find( ex.sounds, s ) ||
|
|
StrList_find( rex.sounds, s ) )
|
|
s.clear();
|
|
}
|
|
|
|
/* write shader */
|
|
stream( g_enginePath, nameOFpack, "_strippedBYrepacker.shader" );
|
|
SaveFile( stream, allShaders.c_str(), allShaders.end() - allShaders.begin() );
|
|
Sys_Printf( "Shaders saved to %s\n", stream.c_str() );
|
|
|
|
if( validateShaders )
|
|
return 0;
|
|
|
|
/* make a pack */
|
|
stream( g_enginePath, nameOFpack, "_repacked.pk3" );
|
|
remove( stream );
|
|
|
|
Sys_Printf( "\n--- ZipZip ---\n" );
|
|
|
|
Sys_Printf( "\n\tShader referenced textures....\n" );
|
|
|
|
for ( const auto& s : pk3Textures ){
|
|
if( !( ( packDups && vfsPackSkyImage( s, stream, compLevel ) ) || ( !packDups && packTexture( s, stream, compLevel, png ) ) ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tPure textures....\n" );
|
|
|
|
for ( const auto& s : pk3Shaders ){
|
|
if ( !s.empty() ){
|
|
if( !( ( packDups && vfsPackSkyImage( s, stream, compLevel ) ) || ( !packDups && packTexture( s, stream, compLevel, png ) ) ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
}
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tSounds....\n" );
|
|
|
|
for ( const auto& s : pk3Sounds ){
|
|
if ( !s.empty() ){
|
|
if ( !packResource( s, stream, compLevel ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
}
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\n\tVideos....\n" );
|
|
|
|
for ( const auto& s : pk3Videos ){
|
|
if ( !packResource( s, stream, compLevel ) ){
|
|
Sys_FPrintf( SYS_WRN, " !FAIL! %s\n", s.c_str() );
|
|
}
|
|
}
|
|
|
|
Sys_Printf( "\nSaved to %s\n", stream.c_str() );
|
|
|
|
if( packDups )
|
|
vfsFindSkyFiles( std::vector<CopiedString>( pk3Textures.cbegin(), pk3Textures.cend() ) );
|
|
|
|
/* return to sender */
|
|
return 0;
|
|
}
|
|
|