/* maketex.cpp - convert textures into DDS images with custom encode Copyright (C) 2015 Uncle Mike This program 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 3 of the License, or (at your option) any later version. This program 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. */ #include "conprint.h" #include #include #include #include #include #include "cmdlib.h" #include "stringlib.h" #include "filesystem.h" #include "imagelib.h" int processed_files = 0; int processed_errors = 0; int CheckSubeSides( const char *filename, char hint ) { const char *ext = COM_FileExtension( filename ); char starthint = 0; char testpath[256]; char cubepath[256]; const imgtype_t *imgtype; int numSides; if( hint >= IMG_SKYBOX_FT && hint <= IMG_SKYBOX_LF ) starthint = IMG_SKYBOX_FT; else if( hint >= IMG_CUBEMAP_PX && hint <= IMG_CUBEMAP_NZ ) starthint = IMG_CUBEMAP_PX; else return 0; // not a skybox or cubemap Q_strncpy( testpath, filename, sizeof( testpath )); COM_StripExtension( testpath ); imgtype = Image_ImageTypeFromHint( starthint ); // NOTE: this safety operation because we just the cutoff end of string if( imgtype ) Q_strncpy( testpath, testpath, Q_strlen( testpath ) - Q_strlen( imgtype->ext ) + 1 ); for( numSides = 0; numSides < 6; numSides++, imgtype++ ) { Q_snprintf( cubepath, sizeof( cubepath ), "%s%s.%s", testpath, imgtype->ext, ext ); if( !COM_FileExists( cubepath )) break; } return numSides; } int ConvertCubeImageToEXT( const char *filename, char hint, const char *outext ) { const char *ext = COM_FileExtension( filename ); char cubepath[256]; char outpath[256]; const imgtype_t *imgtype; rgbdata_t *images[6]; bool skybox = false; rgbdata_t *cube = NULL; int i; memset( images, 0, sizeof( images )); if(( hint != IMG_SKYBOX_FT ) && ( hint != IMG_CUBEMAP_PX )) { // make sure what we have all the sides of cubemap int sides = CheckSubeSides( filename, hint ); return (sides == 6) ? -1 : 2; // don't process these files } else if( CheckSubeSides( filename, hint ) != 6 ) return 2; if( hint == IMG_SKYBOX_FT ) skybox = true; Q_strncpy( outpath, filename, sizeof( outpath )); COM_StripExtension( outpath ); imgtype = Image_ImageTypeFromHint( hint ); // NOTE: this safety operation because we just the cutoff end of string if( imgtype ) Q_strncpy( outpath, outpath, Q_strlen( outpath ) - Q_strlen( imgtype->ext ) + 1 ); if( COM_FileExists( va( "%s.%s", outpath, outext ))) return -1; // already exist Msg( "%s found: %s\n", skybox ? "skybox" : "cubemap", outpath ); for( i = 0; i < 6; i++, imgtype++ ) { Q_snprintf( cubepath, sizeof( cubepath ), "%s%s.%s", outpath, imgtype->ext, ext ); images[i] = COM_LoadImage( cubepath ); if( !images[i] ) break; if( skybox ) MsgDev( D_INFO, "load skyside: %s[%i]\n", cubepath, i ); else MsgDev( D_INFO, "load cubeside: %s[%i]\n", cubepath, i ); } if(( i == 6 ) && ( cube = Image_CreateCubemap( images, skybox )) != NULL ) { int result; if( COM_SaveImage( va( "%s.%s", outpath, outext ), cube )) { MsgDev( D_INFO, "write %s.%s\n", outpath, outext ); result = 1; } else { MsgDev( D_ERROR, "failed to save %s.%s\n", outpath, outext ); result = 0; } Mem_Free( cube ); return result; } else { for( ; i >= 0; i-- ) Mem_Free( images[i] ); // not a cubemap just diffuse return 2; } } int ConvertImageToEXT( const char *filename, const char *outext ) { const char *ext = COM_FileExtension( filename ); rgbdata_t *pic, *alpha = NULL, *gloss = NULL; char outpath[256], lumpname[64]; char maskpath[256], glosspath[256]; // store name for detect suffixes COM_FileBase( filename, lumpname ); char hint = Image_HintFromSuf( lumpname ); if( hint == IMG_ALPHAMASK || hint == IMG_STALKER_GLOSS ) return -1; // ignore to process else if( hint >= IMG_SKYBOX_FT && hint <= IMG_CUBEMAP_NZ ) { int result; result = ConvertCubeImageToEXT( filename, hint, outext ); if( result != 2 ) return result; // continue as normal image hint = IMG_DIFFUSE; } Q_strncpy( outpath, filename, sizeof( outpath )); COM_StripExtension( outpath ); if( COM_FileExists( va( "%s.%s", outpath, outext ))) return -1; // already exist pic = COM_LoadImage( filename ); if( !pic ) { MsgDev( D_REPORT, "couldn't load (%s)\n", filename ); return 0; } MsgDev( D_INFO, "processing %s\n", filename ); // align by 4 pic = Image_Resample( pic, ( pic->width + 15 ) & ~15, ( pic->height + 15 ) & ~15 ); if( hint == IMG_DIFFUSE ) { Q_snprintf( maskpath, sizeof( maskpath ), "%s_mask.%s", outpath, ext ); if( COM_FileExists( maskpath )) alpha = COM_LoadImage( maskpath ); if( alpha ) { // get sizes from the colormap alpha = Image_Resample( alpha, pic->width, pic->height ); MsgDev( D_INFO, "load mask (%s)\n", maskpath ); pic = Image_MergeColorAlpha( pic, alpha ); Mem_Free( alpha ); // COM_SaveImage( va( "%s_merged.tga", outpath ), pic ); if( pic->flags & IMAGE_HAS_8BIT_ALPHA ) MsgDev( D_REPORT, "8-bit alpha detected\n" ); if( pic->flags & IMAGE_HAS_1BIT_ALPHA ) MsgDev( D_REPORT, "1-bit alpha detected\n" ); } else if( pic->flags & IMAGE_HAS_1BIT_ALPHA ) { MsgDev( D_REPORT, "1-bit alpha detected\n" ); } } else if( hint == IMG_STALKER_BUMP ) { Q_snprintf( glosspath, sizeof( glosspath ), "%s#.%s", outpath, ext ); if( COM_FileExists( glosspath )) gloss = COM_LoadImage( glosspath ); if( gloss ) { MsgDev( D_INFO, "load gloss (%s)\n", glosspath ); Image_ConvertBumpStalker( pic, gloss ); Q_strncpy( glosspath, outpath, sizeof( glosspath )); size_t suffix = Q_strlen( glosspath ) - 4; glosspath[suffix] = '\0'; COM_SaveImage( va( "%sgloss.%s", glosspath, outext ), gloss ); MsgDev( D_INFO, "write %sgloss.%s\n", glosspath, outext ); processed_files++; Mem_Free( gloss ); } // replace _bump with _norm size_t suffix = Q_strlen( outpath ) - 4; outpath[suffix] = '\0'; Q_strncat( outpath, "norm", sizeof( outpath )); } int result; if( COM_SaveImage( va( "%s.%s", outpath, outext ), pic )) { MsgDev( D_INFO, "write %s.%s\n", outpath, outext ); result = 1; } else { MsgDev( D_ERROR, "failed to save %s.%s\n", outpath, outext ); result = 0; } Mem_Free( pic ); return result; } int ProcessSearchResults( search_t *search, const char *outext ) { if( !search ) return 0; for( int i = 0; i < search->numfilenames; i++ ) { int result = ConvertImageToEXT( search->filenames[i], outext ); if( result > 0 ) processed_files++; else if( !result ) processed_errors++; } return 1; } void ProcessFiles( const char *srcpath, const char *outext ) { const char *ext = COM_FileExtension( srcpath ); char folders[256], base[256], newpath[256]; // now convert all the files in the root folder search_t *search = COM_Search( srcpath, true ); ProcessSearchResults( search, outext ); Mem_Free( search ); COM_FileBase( srcpath, base ); Q_strncpy( folders, srcpath, sizeof( folders )); COM_StripExtension( folders ); search_t *foldsearch = COM_Search( folders, true ); for( int i = 0; foldsearch != NULL && i < foldsearch->numfilenames; i++ ) { if( COM_FolderExists( foldsearch->filenames[i] )) { Q_snprintf( newpath, sizeof( newpath ), "%s/%s.%s", foldsearch->filenames[i], base, ext ); // now convert all the files in the root folder search_t *subsearch = COM_Search( newpath, true ); ProcessSearchResults( subsearch, outext ); Mem_Free( subsearch ); } } Mem_Free( foldsearch ); } int main( int argc, char **argv ) { char srcpath[256]; bool srcset = false; double start, end; char str[64]; int i; atexit( Sys_CloseLog ); Sys_InitLog( "convert.log" ); COM_InitCmdlib( argv, argc ); Msg( " S.T.A.L.K.E.R texture extractor\n" ); Msg( " XashXT Group 2018(^1c^7)\n\n\n" ); for( i = 1; i < argc; i++ ) { if( !Q_stricmp( argv[i], "-dev" )) { SetDeveloperLevel( atoi( argv[i+1] )); i++; } else if( !srcset ) { Q_strncpy( srcpath, argv[i], sizeof( srcpath )); srcset = true; } else { Msg( "maketex: unknown option %s\n", argv[i] ); break; } } // starting default from the 'textures' folder if( !srcset ) Q_strncpy( srcpath, "*.dds", sizeof( srcpath )); if( i != argc ) { Msg( "Usage: stalker2tga.exe \n" "\nlist options:\n" "^2-dev^7 - shows developer messages\n" "\t\tPress any key to exit" ); system( "pause>nul" ); return 1; } else { BuildGammaTable(); // init gamma conversion helper start = I_FloatTime(); ProcessFiles( srcpath, "tga" ); end = I_FloatTime(); MsgDev( D_INFO, "%3i files processed, %3i errors\n", processed_files, processed_errors ); Q_timestring((int)(end - start), str ); MsgDev( D_INFO, "%s elapsed\n", str ); SetDeveloperLevel( D_REPORT ); Mem_Check(); // report leaks if( !srcset ) { MsgDev( D_INFO, "press any key to exit\n" ); system( "pause>nul" ); } } return 0; }