2019-09-29 03:08:01 +00:00
# include "quakedef.h"
# include "shader.h"
# undef stderr
# define stderr stdout
2019-10-06 01:59:13 +00:00
# include <limits.h>
2019-09-29 03:08:01 +00:00
# ifdef _WIN32
# include <windows.h>
# endif
void VARGS Sys_Error ( const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
vfprintf ( stderr , fmt , argptr ) ;
va_end ( argptr ) ;
fflush ( stderr ) ;
exit ( 1 ) ;
}
void VARGS Con_Printf ( const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
vfprintf ( stderr , fmt , argptr ) ;
va_end ( argptr ) ;
fflush ( stderr ) ;
}
void VARGS Con_DPrintf ( const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
vfprintf ( stderr , fmt , argptr ) ;
va_end ( argptr ) ;
fflush ( stderr ) ;
}
void VARGS Con_ThrottlePrintf ( float * timer , int developerlevel , const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
vfprintf ( stderr , fmt , argptr ) ;
va_end ( argptr ) ;
fflush ( stderr ) ;
}
void * ZF_Malloc ( size_t size )
{
# if defined(__linux__)
void * ret = NULL ;
if ( ! posix_memalign ( & ret , max ( sizeof ( float ) * 4 , sizeof ( void * ) ) , size ) )
memset ( ret , 0 , size ) ;
return ret ;
# else
return calloc ( size , 1 ) ;
# endif
}
void * Z_Malloc ( size_t size )
{
void * r = ZF_Malloc ( size ) ;
if ( ! r )
exit ( 1 ) ;
return r ;
}
void * BZ_Malloc ( size_t size )
{
return Z_Malloc ( size ) ;
}
void * BZF_Malloc ( size_t size )
{
return Z_Malloc ( size ) ;
}
void BZ_Free ( void * p )
{
free ( p ) ;
}
void Z_Free ( void * p )
{
free ( p ) ;
}
# include <sys/stat.h>
void FS_CreatePath ( const char * pname , enum fs_relative relativeto )
{
char * t = strdup ( pname ) , * sl = t ;
while ( ( sl = strchr ( sl , ' / ' ) ) )
{
* sl = 0 ;
# ifdef _WIN32
CreateDirectoryA ( t , NULL ) ;
# else
mkdir ( t , 0777 ) ;
# endif
* sl + + = ' / ' ;
}
free ( t ) ;
}
2019-10-06 01:59:13 +00:00
qboolean FS_Remove ( const char * path , enum fs_relative relativeto )
2019-09-29 03:08:01 +00:00
{
//remove is part of c89.
if ( remove ( path ) = = - 1 )
return false ;
return true ;
}
qboolean FS_NativePath ( const char * fname , enum fs_relative relativeto , char * out , int outlen )
{
Q_strncpyz ( out , fname , outlen ) ;
return true ;
}
# ifdef __unix__
# include <sys/stat.h>
# endif
qbyte * FS_LoadMallocFile ( const char * path , size_t * fsize )
{
qbyte * data = NULL ;
FILE * f ;
# ifdef __unix__
struct stat sb ;
if ( stat ( path , & sb ) < 0 )
return NULL ;
if ( ( sb . st_mode & S_IFMT ) ! = S_IFREG )
return NULL ;
# endif
f = fopen ( path , " rb " ) ;
if ( f )
{
long int sz ;
if ( fseek ( f , 0 , SEEK_END ) > = 0 )
{
sz = ftell ( f ) ;
if ( sz > = 0 )
{
* fsize = sz ;
fseek ( f , 0 , SEEK_SET ) ;
data = ZF_Malloc ( * fsize + 1 ) ;
if ( data )
{
data [ * fsize ] = 0 ;
fread ( data , 1 , * fsize , f ) ;
}
else
Con_Printf ( " Unable to allocate memory for %s \n " , path ) ;
}
}
fclose ( f ) ;
}
return data ;
}
# ifdef _WIN32
dllhandle_t * Sys_LoadLibrary ( const char * name , dllfunction_t * funcs )
{
return NULL ;
}
# else
# include <dlfcn.h>
void Sys_CloseLibrary ( dllhandle_t * lib )
{
dlclose ( ( void * ) lib ) ;
}
dllhandle_t * Sys_LoadLibrary ( const char * name , dllfunction_t * funcs )
{
int i ;
dllhandle_t * lib ;
lib = NULL ;
if ( ! lib )
lib = dlopen ( name , RTLD_LOCAL | RTLD_LAZY ) ;
// if (!lib && !strstr(name, ".so"))
// lib = dlopen (va("%s.so", name), RTLD_LOCAL|RTLD_LAZY);
if ( ! lib )
{
// Con_DPrintf("%s\n", dlerror());
return NULL ;
}
if ( funcs )
{
for ( i = 0 ; funcs [ i ] . name ; i + + )
{
* funcs [ i ] . funcptr = dlsym ( lib , funcs [ i ] . name ) ;
if ( ! * funcs [ i ] . funcptr )
break ;
}
if ( funcs [ i ] . name )
{
Con_DPrintf ( " Unable to find symbol \" %s \" in \" %s \" \n " , funcs [ i ] . name , name ) ;
Sys_CloseLibrary ( ( dllhandle_t * ) lib ) ;
lib = NULL ;
}
}
return ( dllhandle_t * ) lib ;
}
# endif
struct imgfile_s
{
vfsfile_t pub ;
FILE * f ;
} ;
static qboolean QDECL ImgFile_Close ( struct vfsfile_s * file )
{
struct imgfile_s * f = ( struct imgfile_s * ) file ;
fclose ( f - > f ) ;
free ( f ) ;
return true ;
}
static int QDECL ImgFile_WriteBytes ( struct vfsfile_s * file , const void * buffer , int bytestowrite )
{
struct imgfile_s * f = ( struct imgfile_s * ) file ;
return fwrite ( buffer , 1 , bytestowrite , f - > f ) ;
}
2019-10-14 02:36:13 +00:00
static qboolean QDECL ImgFile_Seek ( struct vfsfile_s * file , qofs_t newofs )
{
struct imgfile_s * f = ( struct imgfile_s * ) file ;
if ( fseek ( f - > f , newofs , SEEK_SET ) = = 0 )
return true ; //success
return false ;
}
static qofs_t QDECL ImgFile_Tell ( struct vfsfile_s * file )
{
struct imgfile_s * f = ( struct imgfile_s * ) file ;
return ftell ( f - > f ) ;
}
2019-09-29 03:08:01 +00:00
vfsfile_t * QDECL FS_OpenVFS ( const char * filename , const char * mode , enum fs_relative relativeto )
{
if ( ! strcmp ( mode , " wb " ) )
{
struct imgfile_s * r = malloc ( sizeof ( * r ) ) ;
r - > f = fopen ( filename , mode ) ;
r - > pub . seekstyle = SS_UNSEEKABLE ;
r - > pub . Close = ImgFile_Close ;
r - > pub . WriteBytes = ImgFile_WriteBytes ;
2019-10-14 02:36:13 +00:00
r - > pub . Seek = ImgFile_Seek ;
r - > pub . Tell = ImgFile_Tell ;
2019-09-29 03:08:01 +00:00
if ( r - > f )
return & r - > pub ;
free ( r ) ;
}
return NULL ;
}
qboolean COM_WriteFile ( const char * filename , enum fs_relative fsroot , const void * data , int len )
{
return false ;
}
void QDECL Q_strncpyz ( char * d , const char * s , int n )
{
int i ;
n - - ;
if ( n < 0 )
return ; //this could be an error
for ( i = 0 ; * s ; i + + )
{
if ( i = = n )
break ;
* d + + = * s + + ;
}
* d = ' \0 ' ;
}
void VARGS Q_vsnprintfz ( char * dest , size_t size , const char * fmt , va_list argptr )
{
# ifdef _WIN32
_vsnprintf ( dest , size , fmt , argptr ) ;
# else
# ifdef _DEBUG
if ( ( size_t ) vsnprintf ( dest , size , fmt , argptr ) > size - 1 )
Sys_Error ( " Q_vsnprintfz: truncation " ) ;
# else
vsnprintf ( dest , size , fmt , argptr ) ;
# endif
# endif
dest [ size - 1 ] = 0 ;
}
void VARGS Q_snprintfz ( char * dest , size_t size , const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
Q_vsnprintfz ( dest , size , fmt , argptr ) ;
va_end ( argptr ) ;
}
2019-10-14 02:36:13 +00:00
//palette data is used in lmps, as well as written into pcxes or wads, probably some other things.
2019-09-29 03:08:01 +00:00
qbyte * host_basepal ;
unsigned int d_8to24rgbtable [ 256 ] ;
unsigned int d_8to24bgrtable [ 256 ] ;
2019-10-14 02:36:13 +00:00
static qbyte default_quakepal [ 768 ] =
{ //the quake palette was released into the public domain (or at least gpl) to ease development of tools writing quake-format data.
0 , 0 , 0 , 15 , 15 , 15 , 31 , 31 , 31 , 47 , 47 , 47 , 63 , 63 , 63 , 75 , 75 , 75 , 91 , 91 , 91 , 107 , 107 , 107 , 123 , 123 , 123 , 139 , 139 , 139 , 155 , 155 , 155 , 171 , 171 , 171 , 187 , 187 , 187 , 203 , 203 , 203 , 219 , 219 , 219 , 235 , 235 , 235 , 15 , 11 , 7 , 23 , 15 , 11 , 31 , 23 , 11 , 39 , 27 , 15 , 47 , 35 , 19 , 55 , 43 , 23 , 63 , 47 , 23 , 75 , 55 , 27 , 83 , 59 , 27 , 91 , 67 , 31 , 99 , 75 , 31 , 107 , 83 , 31 , 115 , 87 , 31 , 123 , 95 , 35 , 131 , 103 , 35 , 143 , 111 , 35 , 11 , 11 , 15 , 19 , 19 , 27 , 27 , 27 , 39 , 39 , 39 , 51 , 47 , 47 , 63 , 55 , 55 , 75 , 63 , 63 , 87 , 71 , 71 , 103 , 79 , 79 , 115 , 91 , 91 , 127 , 99 , 99 ,
139 , 107 , 107 , 151 , 115 , 115 , 163 , 123 , 123 , 175 , 131 , 131 , 187 , 139 , 139 , 203 , 0 , 0 , 0 , 7 , 7 , 0 , 11 , 11 , 0 , 19 , 19 , 0 , 27 , 27 , 0 , 35 , 35 , 0 , 43 , 43 , 7 , 47 , 47 , 7 , 55 , 55 , 7 , 63 , 63 , 7 , 71 , 71 , 7 , 75 , 75 , 11 , 83 , 83 , 11 , 91 , 91 , 11 , 99 , 99 , 11 , 107 , 107 , 15 , 7 , 0 , 0 , 15 , 0 , 0 , 23 , 0 , 0 , 31 , 0 , 0 , 39 , 0 , 0 , 47 , 0 , 0 , 55 , 0 , 0 , 63 , 0 , 0 , 71 , 0 , 0 , 79 , 0 , 0 , 87 , 0 , 0 , 95 , 0 , 0 , 103 , 0 , 0 , 111 , 0 , 0 , 119 , 0 , 0 , 127 , 0 , 0 , 19 , 19 , 0 , 27 , 27 , 0 , 35 , 35 , 0 , 47 , 43 , 0 , 55 , 47 , 0 , 67 ,
55 , 0 , 75 , 59 , 7 , 87 , 67 , 7 , 95 , 71 , 7 , 107 , 75 , 11 , 119 , 83 , 15 , 131 , 87 , 19 , 139 , 91 , 19 , 151 , 95 , 27 , 163 , 99 , 31 , 175 , 103 , 35 , 35 , 19 , 7 , 47 , 23 , 11 , 59 , 31 , 15 , 75 , 35 , 19 , 87 , 43 , 23 , 99 , 47 , 31 , 115 , 55 , 35 , 127 , 59 , 43 , 143 , 67 , 51 , 159 , 79 , 51 , 175 , 99 , 47 , 191 , 119 , 47 , 207 , 143 , 43 , 223 , 171 , 39 , 239 , 203 , 31 , 255 , 243 , 27 , 11 , 7 , 0 , 27 , 19 , 0 , 43 , 35 , 15 , 55 , 43 , 19 , 71 , 51 , 27 , 83 , 55 , 35 , 99 , 63 , 43 , 111 , 71 , 51 , 127 , 83 , 63 , 139 , 95 , 71 , 155 , 107 , 83 , 167 , 123 , 95 , 183 , 135 , 107 , 195 , 147 , 123 , 211 , 163 , 139 , 227 , 179 , 151 ,
171 , 139 , 163 , 159 , 127 , 151 , 147 , 115 , 135 , 139 , 103 , 123 , 127 , 91 , 111 , 119 , 83 , 99 , 107 , 75 , 87 , 95 , 63 , 75 , 87 , 55 , 67 , 75 , 47 , 55 , 67 , 39 , 47 , 55 , 31 , 35 , 43 , 23 , 27 , 35 , 19 , 19 , 23 , 11 , 11 , 15 , 7 , 7 , 187 , 115 , 159 , 175 , 107 , 143 , 163 , 95 , 131 , 151 , 87 , 119 , 139 , 79 , 107 , 127 , 75 , 95 , 115 , 67 , 83 , 107 , 59 , 75 , 95 , 51 , 63 , 83 , 43 , 55 , 71 , 35 , 43 , 59 , 31 , 35 , 47 , 23 , 27 , 35 , 19 , 19 , 23 , 11 , 11 , 15 , 7 , 7 , 219 , 195 , 187 , 203 , 179 , 167 , 191 , 163 , 155 , 175 , 151 , 139 , 163 , 135 , 123 , 151 , 123 , 111 , 135 , 111 , 95 , 123 , 99 , 83 , 107 , 87 , 71 , 95 , 75 , 59 , 83 , 63 ,
51 , 67 , 51 , 39 , 55 , 43 , 31 , 39 , 31 , 23 , 27 , 19 , 15 , 15 , 11 , 7 , 111 , 131 , 123 , 103 , 123 , 111 , 95 , 115 , 103 , 87 , 107 , 95 , 79 , 99 , 87 , 71 , 91 , 79 , 63 , 83 , 71 , 55 , 75 , 63 , 47 , 67 , 55 , 43 , 59 , 47 , 35 , 51 , 39 , 31 , 43 , 31 , 23 , 35 , 23 , 15 , 27 , 19 , 11 , 19 , 11 , 7 , 11 , 7 , 255 , 243 , 27 , 239 , 223 , 23 , 219 , 203 , 19 , 203 , 183 , 15 , 187 , 167 , 15 , 171 , 151 , 11 , 155 , 131 , 7 , 139 , 115 , 7 , 123 , 99 , 7 , 107 , 83 , 0 , 91 , 71 , 0 , 75 , 55 , 0 , 59 , 43 , 0 , 43 , 31 , 0 , 27 , 15 , 0 , 11 , 7 , 0 , 0 , 0 , 255 , 11 , 11 , 239 , 19 , 19 , 223 , 27 , 27 , 207 , 35 , 35 , 191 , 43 ,
43 , 175 , 47 , 47 , 159 , 47 , 47 , 143 , 47 , 47 , 127 , 47 , 47 , 111 , 47 , 47 , 95 , 43 , 43 , 79 , 35 , 35 , 63 , 27 , 27 , 47 , 19 , 19 , 31 , 11 , 11 , 15 , 43 , 0 , 0 , 59 , 0 , 0 , 75 , 7 , 0 , 95 , 7 , 0 , 111 , 15 , 0 , 127 , 23 , 7 , 147 , 31 , 7 , 163 , 39 , 11 , 183 , 51 , 15 , 195 , 75 , 27 , 207 , 99 , 43 , 219 , 127 , 59 , 227 , 151 , 79 , 231 , 171 , 95 , 239 , 191 , 119 , 247 , 211 , 139 , 167 , 123 , 59 , 183 , 155 , 55 , 199 , 195 , 55 , 231 , 227 , 87 , 127 , 191 , 255 , 171 , 231 , 255 , 215 , 255 , 255 , 103 , 0 , 0 , 139 , 0 , 0 , 179 , 0 , 0 , 215 , 0 , 0 , 255 , 0 , 0 , 255 , 243 , 147 , 255 , 247 , 199 , 255 , 255 , 255 , 159 , 91 , 83
} ;
2019-09-29 03:08:01 +00:00
qbyte GetPaletteIndexNoFB ( int red , int green , int blue )
{
2019-10-14 02:36:13 +00:00
int i ;
int best = 0 ;
int bestdist = INT_MAX ;
int dist ;
for ( i = 0 ; i < 256 - 32 ; i + + )
{
dist =
abs ( host_basepal [ i * 3 + 0 ] - red ) +
abs ( host_basepal [ i * 3 + 1 ] - green ) +
abs ( host_basepal [ i * 3 + 2 ] - blue ) ;
if ( dist < bestdist )
{
bestdist = dist ;
best = i ;
}
}
return best ;
}
static void ImgTool_SetupPalette ( void )
{
int i ;
//we ought to try to read gfx/palette.lmp, but its probably in a pak
host_basepal = default_quakepal ;
for ( i = 0 ; i < 256 ; i + + )
{
d_8to24rgbtable [ i ] = ( host_basepal [ i * 3 + 0 ] < < 0 ) | ( host_basepal [ i * 3 + 1 ] < < 8 ) | ( host_basepal [ i * 3 + 2 ] < < 16 ) ;
d_8to24bgrtable [ i ] = ( host_basepal [ i * 3 + 0 ] < < 16 ) | ( host_basepal [ i * 3 + 1 ] < < 8 ) | ( host_basepal [ i * 3 + 2 ] < < 0 ) ;
}
}
static void ImgTool_FreeMips ( struct pendingtextureinfo * mips )
{
size_t i ;
2019-10-18 08:37:38 +00:00
if ( mips )
{
for ( i = 0 ; i < mips - > mipcount ; i + + )
if ( mips - > mip [ i ] . needfree )
BZ_Free ( mips - > mip [ i ] . data ) ;
if ( mips - > extrafree )
BZ_Free ( mips - > extrafree ) ;
BZ_Free ( mips ) ;
}
2019-09-29 03:08:01 +00:00
}
sh_config_t sh_config ;
viddef_t vid ;
2019-10-18 08:37:38 +00:00
static const char * imagetypename [ ] = { " 2D " , " 3D " , " Cube " , " 2DArray " , " CubemapArray " , " INVALID " , " INVALID " , " INVALID " , " INVALID " , " INVALID " } ;
2019-09-29 03:08:01 +00:00
struct opts_s
{
2019-10-18 08:37:38 +00:00
int textype ;
2019-09-29 03:08:01 +00:00
unsigned int flags ; //image flags to use (affects how textures get interpreted a little)
unsigned int mipnum ; //when exporting to a mipless format, this is the mip level that is actually written. default 0.
uploadfmt_t newpixelformat ; //try to convert to this pixel format on export.
} ;
void Image_GenerateMips ( struct pendingtextureinfo * mips , unsigned int flags ) ;
int Image_WritePNG ( const char * filename , enum fs_relative fsroot , int compression , void * * buffers , int numbuffers , qintptr_t bufferstride , int width , int height , enum uploadfmt fmt , qboolean writemetadata ) ;
qboolean WriteTGA ( const char * filename , enum fs_relative fsroot , const qbyte * fte_restrict rgb_buffer , qintptr_t bytestride , int width , int height , enum uploadfmt fmt ) ;
2019-10-06 01:59:13 +00:00
static enum uploadfmt ImgTool_ASTCToLDR ( uploadfmt_t fmt )
2019-09-29 03:08:01 +00:00
{
if ( fmt > = PTI_ASTC_FIRST & & fmt < = PTI_ASTC_LAST )
{
if ( fmt > = PTI_ASTC_4X4_HDR )
return ( fmt - PTI_ASTC_4X4_HDR ) + PTI_ASTC_4X4_LDR ;
if ( fmt > = PTI_ASTC_4X4_SRGB )
return ( fmt - PTI_ASTC_4X4_SRGB ) + PTI_ASTC_4X4_LDR ;
}
2019-10-06 01:59:13 +00:00
if ( fmt = = PTI_BC1_RGB )
return PTI_BC1_RGBA ;
2019-09-29 03:08:01 +00:00
return fmt ;
}
# ifdef _WIN32
# include <fcntl.h>
static void FS_MakeTempName ( char * out , size_t outsize , char * prefix , char * suffix )
{
int fd ;
unsigned int n ;
unsigned int s = rand ( ) ;
for ( n = 0 ; n < 0xffffff ; n + + )
{
Q_snprintfz ( out , outsize , " /tmp/%s%06x%s " , prefix , ( n + s ) & 0xffffff , suffix ) ;
fd = _open ( out , _O_CREAT | _O_EXCL , _S_IREAD | _S_IWRITE ) ;
if ( fd = = - 1 )
continue ;
close ( fd ) ;
return ;
}
Sys_Error ( " FS_MakeTempName failed \n " ) ;
}
# else
# include <unistd.h>
static void FS_MakeTempName ( char * out , size_t outsize , char * prefix , char * suffix )
{
snprintf ( out , outsize , " /tmp/%sXXXXXX%s " , prefix , suffix ) ;
close ( mkstemps ( out , strlen ( suffix ) ) ) ; //bsd4.3/posix1-2001
}
# endif
2019-10-06 01:59:13 +00:00
static qboolean ImgTool_ConvertPixelFormat ( struct opts_s * args , const char * inname , struct pendingtextureinfo * mips )
2019-09-29 03:08:01 +00:00
{
struct pendingtextureinfo tmp , * ret ;
size_t m ;
char raw [ MAX_OSPATH ] ;
char comp [ MAX_OSPATH ] ;
char command [ MAX_OSPATH * 3 ] ;
qbyte * fdata ;
size_t fsize ;
int bb , bw , bh ;
qboolean canktx = false ;
uploadfmt_t targfmt = args - > newpixelformat ;
2019-10-18 08:37:38 +00:00
int d , l , layers ;
2019-09-29 03:08:01 +00:00
2019-10-06 01:59:13 +00:00
//force it to bc1 if bc2 or bc3 with no alpha channel.
if ( ( targfmt = = PTI_BC2_RGBA | | targfmt = = PTI_BC3_RGBA ) & & ! Image_FormatHasAlpha ( mips - > encoding ) )
targfmt = PTI_BC1_RGB ;
2019-09-29 03:08:01 +00:00
if ( targfmt > = PTI_ASTC_FIRST & & targfmt < = PTI_ASTC_LAST )
{
Q_snprintfz ( command , sizeof ( command ) , " astcenc -c " ) ;
canktx = true ;
}
else if ( targfmt = = PTI_BC1_RGB )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc1%s " , ( args - > flags & IF_TRYBUMP ) ? " n " : " " ) ;
else if ( targfmt = = PTI_BC1_RGBA )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc1a " ) ;
else if ( targfmt = = PTI_BC2_RGBA )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc2 " ) ;
else if ( targfmt = = PTI_BC3_RGBA )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc3%s " , ( args - > flags & IF_TRYBUMP ) ? " n " : " " ) ;
else if ( targfmt = = PTI_BC4_R8 )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc4 " ) ;
else if ( targfmt = = PTI_BC5_RG8 )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc5 " ) ;
2019-10-06 01:59:13 +00:00
else if ( targfmt = = PTI_BC6_RGB_SFLOAT | | targfmt = = PTI_BC6_RGB_UFLOAT )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc6 " ) ;
else if ( targfmt = = PTI_BC7_RGBA )
Q_snprintfz ( command , sizeof ( command ) , " nvcompress -bc7 " ) ;
2019-09-29 03:08:01 +00:00
else
2019-10-14 02:36:13 +00:00
{
if ( mips - > encoding ! = targfmt )
{
for ( m = 0 ; m < PTI_MAX ; m + + )
sh_config . texfmt [ m ] = ( m = = targfmt ) ;
Image_ChangeFormat ( mips , args - > flags , PTI_INVALID , inname ) ;
if ( mips - > encoding = = targfmt )
return true ;
//switch to common formats...
for ( m = 0 ; m < PTI_MAX ; m + + )
sh_config . texfmt [ m ] = ( m = = targfmt ) | | ( m = = PTI_RGBA8 ) ;
Image_ChangeFormat ( mips , args - > flags , PTI_INVALID , inname ) ;
//and try again...
for ( m = 0 ; m < PTI_MAX ; m + + )
sh_config . texfmt [ m ] = ( m = = targfmt ) ;
Image_ChangeFormat ( mips , args - > flags , PTI_INVALID , inname ) ;
return ( mips - > encoding = = targfmt ) ;
}
2019-10-06 01:59:13 +00:00
return false ;
2019-10-14 02:36:13 +00:00
}
2019-09-29 03:08:01 +00:00
if ( canktx )
FS_MakeTempName ( raw , sizeof ( raw ) , " itr " , " .ktx " ) ;
else
FS_MakeTempName ( raw , sizeof ( raw ) , " itr " , " .png " ) ;
FS_MakeTempName ( comp , sizeof ( comp ) , " itc " , " .ktx " ) ;
tmp . type = mips - > type ;
tmp . encoding = mips - > encoding ;
tmp . extrafree = NULL ;
tmp . mipcount = 1 ;
Image_BlockSizeForEncoding ( targfmt , & bb , & bw , & bh ) ;
Q_snprintfz ( command + strlen ( command ) , sizeof ( command ) - strlen ( command ) , " \" %s \" \" %s \" " , raw , comp ) ;
if ( targfmt > = PTI_ASTC_FIRST & & targfmt < = PTI_ASTC_LAST )
Q_snprintfz ( command + strlen ( command ) , sizeof ( command ) - strlen ( command ) , " %ix%i -exhaustive " , bw , bh ) ;
if ( targfmt > = PTI_ASTC_4X4_SRGB & & targfmt < = PTI_ASTC_12X12_SRGB )
Q_strncatz ( command , " -srgb " , sizeof ( command ) ) ;
if ( targfmt > = PTI_ASTC_4X4_HDR & & targfmt < = PTI_ASTC_12X12_HDR )
Q_strncatz ( command , " -hdr " , sizeof ( command ) ) ;
2019-10-06 01:59:13 +00:00
if ( targfmt > = PTI_BC1_RGB & & targfmt < = PTI_BC7_RGBA_SRGB & & ( strstr ( inname , " _n. " ) | | strstr ( inname , " _norm. " ) ) )
Q_strncatz ( command , " -normal " , sizeof ( command ) ) ; //looks like a normalmap... tweak metrics to favour normalised results.
2019-09-29 03:08:01 +00:00
Q_strncatz ( command , " >> /dev/null " , sizeof ( command ) ) ;
2019-10-14 02:36:13 +00:00
if ( ! canktx )
{
//make sure the source pixel format is acceptable if we're forced to write a png
for ( m = 0 ; m < PTI_MAX ; m + + )
sh_config . texfmt [ m ] =
( m = = PTI_RGBA8 ) | | ( m = = PTI_RGBX8 ) | |
( m = = PTI_BGRA8 ) | | ( m = = PTI_BGRX8 ) | |
( m = = PTI_LLLA8 ) | | ( m = = PTI_LLLX8 ) | |
( m = = PTI_RGBA16 ) | |
( m = = PTI_L8 ) | | ( m = = PTI_L8A8 ) | |
/*(m == PTI_L16) ||*/
( m = = PTI_BGR8 ) | | ( m = = PTI_BGR8 ) | |
0 ;
Image_ChangeFormat ( mips , args - > flags & ~ IF_PREMULTIPLYALPHA , PTI_INVALID , inname ) ;
//make sure we properly read whatever is given back to us
for ( m = 1 ; m < countof ( sh_config . texfmt ) ; m + + )
sh_config . texfmt [ m ] = true ;
}
2019-09-29 03:08:01 +00:00
Image_BlockSizeForEncoding ( mips - > encoding , & bb , & bw , & bh ) ;
for ( m = 0 ; m < mips - > mipcount ; m + + )
{
2019-10-18 08:37:38 +00:00
qbyte * srcdata = mips - > mip [ m ] . data ;
size_t srcsize = mips - > mip [ m ] . datasize ;
if ( mips - > type = = PTI_3D )
2019-09-29 03:08:01 +00:00
{
2019-10-18 08:37:38 +00:00
layers = 1 ;
d = mips - > mip [ m ] . depth ;
tmp . type = PTI_2D ;
2019-09-29 03:08:01 +00:00
}
else
{
2019-10-18 08:37:38 +00:00
layers = mips - > mip [ m ] . depth ;
d = 1 ;
tmp . type = PTI_2D ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
for ( l = 0 ; l < layers ; l + + )
{
Con_DPrintf ( " Compressing %s mip %u, layer %u \n " , inname , ( unsigned ) m , l ) ;
tmp . mip [ 0 ] = mips - > mip [ m ] ;
tmp . mip [ 0 ] . needfree = false ;
tmp . mip [ 0 ] . depth = d ;
tmp . mip [ 0 ] . datasize = srcsize / layers ;
tmp . mip [ 0 ] . data = srcdata + l * tmp . mip [ 0 ] . datasize ;
( void ) tmp ;
if ( canktx )
{
# ifdef IMAGEFMT_KTX
if ( ! Image_WriteKTXFile ( raw , FS_SYSTEM , & tmp ) )
# endif
break ;
}
else
{
# ifdef AVAIL_PNGLIB
if ( ! Image_WritePNG ( raw , FS_SYSTEM , 0 , & tmp . mip [ 0 ] . data , 1 , tmp . mip [ 0 ] . width * bb , tmp . mip [ 0 ] . width , tmp . mip [ 0 ] . height , tmp . encoding , false ) )
# endif
break ;
}
2019-09-29 03:08:01 +00:00
2019-10-18 08:37:38 +00:00
system ( command ) ;
2019-09-29 03:08:01 +00:00
2019-10-18 08:37:38 +00:00
fdata = FS_LoadMallocFile ( comp , & fsize ) ;
ret = Image_LoadMipsFromMemory ( IF_NOMIPMAP , comp , comp , fdata , fsize ) ;
if ( ret & & ret - > mip [ 0 ] . width = = mips - > mip [ m ] . width & &
ret - > mip [ 0 ] . height = = mips - > mip [ m ] . height & &
ret - > mip [ 0 ] . depth = = d & &
ImgTool_ASTCToLDR ( ret - > encoding ) = = ImgTool_ASTCToLDR ( targfmt ) )
{
if ( layers = = 1 ) //just copy it over. FIXME: memory leak
mips - > mip [ m ] = ret - > mip [ 0 ] ;
else
{
if ( ! l )
{
mips - > mip [ m ] . datasize = ret - > mip [ 0 ] . datasize * layers ;
mips - > mip [ m ] . data = BZ_Malloc ( mips - > mip [ m ] . datasize ) ;
mips - > mip [ m ] . needfree = true ;
}
else if ( ret - > mip [ 0 ] . datasize ! = mips - > mip [ m ] . datasize / layers )
break ; //erk..?
memcpy ( mips - > mip [ m ] . data + l * ret - > mip [ 0 ] . datasize , ret - > mip [ 0 ] . data , ret - > mip [ 0 ] . datasize ) ;
}
continue ;
}
break ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
if ( l ! = layers )
break ;
2019-09-29 03:08:01 +00:00
}
mips - > encoding = targfmt ;
mips - > mipcount = m ;
if ( mips - > mipcount & & targfmt > = PTI_BC1_RGB & & targfmt < = PTI_BC7_RGBA_SRGB )
{ //d3d has some annoying limitations.
//do not warn for astc files, their block sizes are too weird.
Image_BlockSizeForEncoding ( targfmt , & bb , & bw , & bh ) ;
if ( mips - > mip [ 0 ] . width % bw | | mips - > mip [ 0 ] . height % bh )
2019-10-06 01:59:13 +00:00
Con_Printf ( " %s: mip0 of %i*%i is not a multiple of %i*%i (d3d warning) \n " , inname , mips - > mip [ 0 ] . width , mips - > mip [ 0 ] . height , bw , bh ) ;
2019-09-29 03:08:01 +00:00
}
2019-10-06 01:59:13 +00:00
FS_Remove ( raw , FS_SYSTEM ) ;
FS_Remove ( comp , FS_SYSTEM ) ;
return true ;
2019-09-29 03:08:01 +00:00
}
const char * COM_GetFileExtension ( const char * in , const char * term )
{
const char * dot ;
if ( ! term )
term = in + strlen ( in ) ;
for ( dot = term - 1 ; dot > = in & & * dot ! = ' / ' & & * dot ! = ' \\ ' ; dot - - )
{
if ( * dot = = ' . ' )
return dot ;
}
return " " ;
}
2019-10-18 08:37:38 +00:00
static struct pendingtextureinfo * ImgTool_Read ( struct opts_s * args , const char * inname )
2019-09-29 03:08:01 +00:00
{
qbyte * indata ;
2019-10-18 08:37:38 +00:00
size_t fsize ;
2019-09-29 03:08:01 +00:00
struct pendingtextureinfo * in ;
2019-10-18 08:37:38 +00:00
indata = FS_LoadMallocFile ( inname , & fsize ) ;
if ( ! indata )
printf ( " %s: unable to read \n " , inname ) ;
else
{
in = Image_LoadMipsFromMemory ( args - > flags , inname , inname , indata , fsize ) ;
if ( ! in )
{
printf ( " %s: unsupported format \n " , inname ) ;
BZ_Free ( indata ) ;
}
else
{
printf ( " %s: %s %s, %i*%i, %i mips \n " , inname , imagetypename [ in - > type ] , Image_FormatName ( in - > encoding ) , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height , in - > mipcount ) ;
return in ;
}
}
return NULL ;
}
static struct pendingtextureinfo * ImgTool_Combine ( struct opts_s * args , const char * * namelist , unsigned int filecount )
{
struct pendingtextureinfo * r , * t ;
unsigned int i ;
unsigned int layers = 0 ;
unsigned int j = 0 ;
2019-10-06 01:59:13 +00:00
2019-10-18 08:37:38 +00:00
struct
{
const char * fname ;
struct pendingtextureinfo * in ;
} * srcs , * tmpsrcs ;
2019-09-29 03:08:01 +00:00
2019-10-18 08:37:38 +00:00
if ( args - > textype = = PTI_3D )
args - > flags | = IF_NOMIPMAP ; //generate any mipmaps after...
srcs = alloca ( sizeof ( * srcs ) * filecount ) ;
for ( i = 0 , j = 0 ; i < filecount ; i + + )
2019-09-29 03:08:01 +00:00
{
2019-10-18 08:37:38 +00:00
srcs [ j ] . in = t = ImgTool_Read ( args , namelist [ i ] ) ;
if ( srcs [ j ] . in )
2019-09-29 03:08:01 +00:00
{
2019-10-18 08:37:38 +00:00
if ( ! j )
{
//get the image loader to massage pixel formats...
memset ( sh_config . texfmt , 0 , sizeof ( sh_config . texfmt ) ) ;
sh_config . texfmt [ srcs [ j ] . in - > encoding ] = true ;
}
else if ( ! t - > mipcount | | ! t - > mip [ 0 ] . data )
{
Con_Printf ( " %s: no valid image data \n " , namelist [ i ] ) ;
ImgTool_FreeMips ( srcs [ j ] . in ) ;
continue ;
}
else if ( t - > encoding ! = srcs [ 0 ] . in - > encoding )
{
Con_Printf ( " %s: mismatched pixel format, (%s not %s) cannot combine \n " , namelist [ i ] , Image_FormatName ( t - > encoding ) , Image_FormatName ( srcs [ 0 ] . in - > encoding ) ) ;
ImgTool_FreeMips ( srcs [ j ] . in ) ;
continue ;
}
else if ( t - > type = = PTI_CUBE & & t - > mip [ 0 ] . depth ! = 6 )
{
Con_Printf ( " %s: incorrect cubemap data \n " , namelist [ i ] ) ;
ImgTool_FreeMips ( srcs [ j ] . in ) ;
continue ;
}
else if ( srcs [ 0 ] . in - > mip [ 0 ] . width ! = t - > mip [ 0 ] . width | | srcs [ 0 ] . in - > mip [ 0 ] . height ! = t - > mip [ 0 ] . height )
{
Con_Printf ( " %s: incorrect image size \n " , namelist [ i ] ) ;
ImgTool_FreeMips ( srcs [ j ] . in ) ;
continue ;
}
else if ( t - > mip [ 0 ] . depth = = 0 )
{
Con_Printf ( " %s: no layers \n " , namelist [ i ] ) ;
ImgTool_FreeMips ( srcs [ j ] . in ) ;
continue ;
}
layers + = t - > mip [ 0 ] . depth ;
srcs [ j + + ] . fname = namelist [ i ] ;
}
}
filecount = j ;
2019-09-29 03:08:01 +00:00
2019-10-18 08:37:38 +00:00
//FIXME: reorder input images to handle ft/bk/lt/rt/up/dn, and flip+rotate+etc to match quake
2019-10-14 02:36:13 +00:00
2019-10-18 08:37:38 +00:00
if ( args - > textype = = PTI_CUBE )
{
int facetype [ 6 ] ;
static const struct { qboolean flipx , flipy , flipd ; } skyboxflips [ ] = {
{ true , false , true } ,
{ false , true , true } ,
{ true , true , false } ,
{ false , false , false } ,
{ true , false , true } ,
{ true , false , true }
} ;
for ( i = 0 ; i < 6 ; i + + )
facetype [ i ] = 0 ;
for ( i = 0 ; i < filecount ; i + + )
{
const char * ex = COM_GetFileExtension ( srcs [ i ] . fname , NULL ) ;
if ( ex & & ex - srcs [ i ] . fname > 2 & & srcs [ i ] . in - > mip [ 0 ] . depth = = 1 )
2019-10-14 02:36:13 +00:00
{
2019-10-18 08:37:38 +00:00
if ( ! strncasecmp ( ex - 2 , " rt " , 2 ) )
facetype [ 0 ] = - i - 1 ;
else if ( ! strncasecmp ( ex - 2 , " lf " , 2 ) )
facetype [ 1 ] = - i - 1 ;
else if ( ! strncasecmp ( ex - 2 , " ft " , 2 ) )
facetype [ 2 ] = - i - 1 ;
else if ( ! strncasecmp ( ex - 2 , " bk " , 2 ) )
facetype [ 3 ] = - i - 1 ;
else if ( ! strncasecmp ( ex - 2 , " up " , 2 ) )
facetype [ 4 ] = - i - 1 ;
else if ( ! strncasecmp ( ex - 2 , " dn " , 2 ) )
facetype [ 5 ] = - i - 1 ;
else if ( ! strncasecmp ( ex - 2 , " px " , 2 ) )
facetype [ 0 ] = i + 1 ;
else if ( ! strncasecmp ( ex - 2 , " nx " , 2 ) )
facetype [ 1 ] = i + 1 ;
else if ( ! strncasecmp ( ex - 2 , " py " , 2 ) )
facetype [ 2 ] = i + 1 ;
else if ( ! strncasecmp ( ex - 2 , " ny " , 2 ) )
facetype [ 3 ] = i + 1 ;
else if ( ! strncasecmp ( ex - 2 , " pz " , 2 ) )
facetype [ 4 ] = i + 1 ;
else if ( ! strncasecmp ( ex - 2 , " nz " , 2 ) )
facetype [ 5 ] = i + 1 ;
2019-10-14 02:36:13 +00:00
}
2019-10-18 08:37:38 +00:00
}
if ( facetype [ 0 ] & & facetype [ 1 ] & & facetype [ 2 ] & & facetype [ 3 ] & & facetype [ 4 ] & & facetype [ 5 ] )
{
Con_Printf ( " Reordering images to match cubemap \n " ) ;
tmpsrcs = alloca ( sizeof ( * tmpsrcs ) * filecount ) ;
memcpy ( tmpsrcs , srcs , sizeof ( * tmpsrcs ) * filecount ) ;
for ( i = 0 ; i < 6 ; i + + )
2019-10-14 02:36:13 +00:00
{
2019-10-18 08:37:38 +00:00
if ( facetype [ i ] < 0 )
{ //flip to match legacy skyboxes
unsigned bb , bw , bh ;
srcs [ i ] = tmpsrcs [ - facetype [ i ] - 1 ] ;
t = srcs [ i ] . in ;
Image_BlockSizeForEncoding ( t - > encoding , & bb , & bw , & bh ) ;
if ( bw = = 1 & & bh = = 1 )
{
for ( j = 0 ; j < t - > mipcount ; j + + )
{
void * data = Image_FlipImage ( t - > mip [ j ] . data , BZ_Malloc ( t - > mip [ j ] . datasize ) , & t - > mip [ j ] . width , & t - > mip [ j ] . height , bb , skyboxflips [ i ] . flipx , skyboxflips [ i ] . flipy , skyboxflips [ i ] . flipd ) ;
if ( t - > mip [ j ] . needfree )
Z_Free ( t - > mip [ j ] . data ) ;
t - > mip [ j ] . data = data ;
t - > mip [ j ] . needfree = true ;
}
}
}
else
srcs [ i ] = tmpsrcs [ facetype [ i ] - 1 ] ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
}
else
Con_Printf ( " WARNING: Cubemap ordering unknown! \n " ) ;
}
2019-10-14 02:36:13 +00:00
2019-10-18 08:37:38 +00:00
if ( ! filecount )
Con_Printf ( " no valid input files \n " ) ;
else if ( ! layers )
Con_Printf ( " Images must have at least one layer \n " ) ;
else if ( args - > textype = = PTI_2D & & layers ! = 1 )
Con_Printf ( " 2D images must have one layer exactly, sorry \n " ) ;
else if ( args - > textype = = PTI_CUBE & & layers ! = 6 )
Con_Printf ( " Cubemaps must have 6 layers exactly \n " ) ;
else if ( args - > textype = = PTI_CUBE_ARRAY & & layers % 6 )
Con_Printf ( " Cubemap arrays must have a multiple of 6 layers exactly \n " ) ;
else
{
t = srcs [ 0 ] . in ;
r = Z_Malloc ( sizeof ( * t ) ) ;
r - > type = args - > textype ;
r - > extrafree = NULL ;
r - > encoding = t - > encoding ;
if ( args - > textype = = PTI_3D )
r - > mipcount = 1 ;
else
r - > mipcount = t - > mipcount ;
for ( j = 0 ; j < t - > mipcount ; j + + )
{
r - > mip [ j ] . datasize = t - > mip [ j ] . datasize * layers ;
r - > mip [ j ] . width = t - > mip [ j ] . width ;
r - > mip [ j ] . height = t - > mip [ j ] . height ;
r - > mip [ j ] . depth = 0 ;
r - > mip [ j ] . needfree = true ;
r - > mip [ j ] . data = BZ_Malloc ( r - > mip [ j ] . datasize ) ;
}
2019-09-29 03:08:01 +00:00
2019-10-18 08:37:38 +00:00
for ( i = 0 , j = 0 ; i < filecount ; i + + )
{
t = srcs [ i ] . in ;
if ( ! t )
2019-09-29 03:08:01 +00:00
{
2019-10-18 08:37:38 +00:00
ImgTool_FreeMips ( r ) ;
return NULL ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
for ( j = 0 ; j < r - > mipcount ; j + + )
2019-10-14 02:36:13 +00:00
{
2019-10-18 08:37:38 +00:00
if ( r - > mip [ j ] . width ! = t - > mip [ j ] . width | | r - > mip [ j ] . height ! = t - > mip [ j ] . height | | t - > mip [ j ] . depth ! = 1 )
{
Con_Printf ( " %s: mismatched mipmap sizes \n " , namelist [ i ] ) ;
continue ;
}
memcpy ( r - > mip [ j ] . data + t - > mip [ j ] . datasize * r - > mip [ j ] . depth , t - > mip [ j ] . data , t - > mip [ j ] . datasize ) ;
r - > mip [ j ] . depth + + ;
2019-10-14 02:36:13 +00:00
}
2019-10-18 08:37:38 +00:00
}
for ( i = 0 ; i < filecount ; i + + )
ImgTool_FreeMips ( srcs [ i ] . in ) ;
printf ( " %s: %s %s, %i*%i, %i mips \n " , " combined " , imagetypename [ r - > type ] , Image_FormatName ( r - > encoding ) , r - > mip [ 0 ] . width , r - > mip [ 0 ] . height , r - > mipcount ) ;
return r ;
}
for ( i = 0 ; i < filecount ; i + + )
ImgTool_FreeMips ( srcs [ i ] . in ) ;
return NULL ;
}
static void ImgTool_Convert ( struct opts_s * args , struct pendingtextureinfo * in , const char * inname , const char * outname )
{
size_t k ;
const char * outext = COM_GetFileExtension ( outname , NULL ) ;
qboolean allowcompressed = false ;
if ( ! strcmp ( outext , " .dds " ) | | ! strcmp ( outext , " .ktx " ) )
allowcompressed = true ;
if ( in )
{
if ( ! ( args - > flags & IF_NOMIPMAP ) & & in - > mipcount = = 1 )
Image_GenerateMips ( in , args - > flags ) ;
if ( args - > mipnum > = in - > mipcount )
{
ImgTool_FreeMips ( in ) ;
Con_Printf ( " %s: Requested output mip number was out of bounds %i >= %i \n " , outname , args - > mipnum , in - > mipcount ) ;
return ;
}
for ( k = 0 ; k < args - > mipnum ; k + + )
{
if ( in - > mip [ k ] . needfree )
BZ_Free ( in - > mip [ k ] . data ) ;
}
in - > mipcount - = k ;
memmove ( in - > mip , & in - > mip [ k ] , sizeof ( in - > mip [ 0 ] ) * in - > mipcount ) ;
if ( args - > newpixelformat ! = PTI_INVALID & & ( args - > newpixelformat < PTI_BC1_RGB | | allowcompressed ) & & ImgTool_ConvertPixelFormat ( args , inname , in ) )
printf ( " \t (Converted to %s) \n " , Image_FormatName ( in - > encoding ) ) ;
if ( ! in - > mipcount )
{
ImgTool_FreeMips ( in ) ;
printf ( " %s: unable to convert any mips \n " , inname ) ;
return ;
}
if ( 0 )
;
# ifdef IMAGEFMT_KTX
else if ( ! strcmp ( outext , " .ktx " ) )
{
if ( ! Image_WriteKTXFile ( outname , FS_SYSTEM , in ) )
Con_Printf ( " %s(%s): Write failed \n " , outname , Image_FormatName ( in - > encoding ) ) ;
}
2019-10-06 01:59:13 +00:00
# endif
# ifdef IMAGEFMT_DDS
2019-10-18 08:37:38 +00:00
else if ( ! strcmp ( outext , " .dds " ) )
{
if ( ! Image_WriteDDSFile ( outname , FS_SYSTEM , in ) )
Con_Printf ( " %s(%s): Write failed \n " , outname , Image_FormatName ( in - > encoding ) ) ;
}
2019-10-06 01:59:13 +00:00
# endif
2019-10-18 08:37:38 +00:00
else
{
int bb , bw , bh ;
2019-10-14 02:36:13 +00:00
2019-10-18 08:37:38 +00:00
if ( in - > type ! = PTI_2D )
Con_Printf ( " %s: Unable to write %s file to 2d image format \n " , outname , imagetypename [ in - > type ] ) ;
2019-10-06 01:59:13 +00:00
# ifdef IMAGEFMT_PNG
2019-10-18 08:37:38 +00:00
else if ( ! strcmp ( outext , " .png " ) )
{
2019-09-29 03:08:01 +00:00
# ifdef AVAIL_PNGLIB
2019-10-18 08:37:38 +00:00
//force the format, because we can.
for ( k = 0 ; k < PTI_MAX ; k + + )
sh_config . texfmt [ k ] =
( k = = PTI_RGBA8 ) | | ( k = = PTI_RGBX8 ) | |
( k = = PTI_BGRA8 ) | | ( k = = PTI_BGRX8 ) | |
( k = = PTI_LLLA8 ) | | ( k = = PTI_LLLX8 ) | |
( k = = PTI_RGBA16 ) | |
( k = = PTI_L8 ) | | ( k = = PTI_L8A8 ) | |
/*(k == PTI_L16) ||*/
( k = = PTI_BGR8 ) | | ( k = = PTI_BGR8 ) | |
0 ;
if ( ! sh_config . texfmt [ in - > encoding ] )
{
Image_ChangeFormat ( in , args - > flags & ~ IF_PREMULTIPLYALPHA , PTI_INVALID , outname ) ;
printf ( " \t (Exporting as %s) \n " , Image_FormatName ( in - > encoding ) ) ;
2019-10-14 02:36:13 +00:00
}
2019-10-18 08:37:38 +00:00
Image_BlockSizeForEncoding ( in - > encoding , & bb , & bw , & bh ) ;
if ( ! Image_WritePNG ( outname , FS_SYSTEM , 0 , & in - > mip [ 0 ] . data , 1 , in - > mip [ 0 ] . width * bb , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height , in - > encoding , false ) )
# endif
Con_Printf ( " %s(%s): Write failed \n " , outname , Image_FormatName ( in - > encoding ) ) ;
}
2019-10-06 01:59:13 +00:00
# endif
# ifdef IMAGEFMT_TGA
2019-10-18 08:37:38 +00:00
else if ( ! strcmp ( outext , " .tga " ) )
{
for ( k = 0 ; k < PTI_MAX ; k + + )
sh_config . texfmt [ k ] =
( k = = PTI_RGBA8 ) | | ( k = = PTI_RGBX8 ) | |
( k = = PTI_BGRA8 ) | | ( k = = PTI_BGRX8 ) | |
( k = = PTI_LLLA8 ) | | ( k = = PTI_LLLX8 ) | |
( k = = PTI_RGBA16F ) | | ( k = = PTI_R16F ) | | //half-float tgas is a format extension, but allow it.
( k = = PTI_L8 ) | | ( k = = PTI_L8A8 ) | |
/*(k == PTI_L16) ||*/
( k = = PTI_BGR8 ) | | ( k = = PTI_BGR8 ) | |
0 ;
if ( ! sh_config . texfmt [ in - > encoding ] )
2019-10-14 02:36:13 +00:00
{
2019-10-18 08:37:38 +00:00
Image_ChangeFormat ( in , args - > flags & ~ IF_PREMULTIPLYALPHA , PTI_INVALID , outname ) ;
printf ( " \t (Exporting as %s) \n " , Image_FormatName ( in - > encoding ) ) ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
Image_BlockSizeForEncoding ( in - > encoding , & bb , & bw , & bh ) ;
if ( ! WriteTGA ( outname , FS_SYSTEM , in - > mip [ 0 ] . data , in - > mip [ 0 ] . width * bb , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height , in - > encoding ) )
Con_Printf ( " %s(%s): Write failed \n " , outname , Image_FormatName ( in - > encoding ) ) ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
# endif
else
Con_Printf ( " %s: Unknown output file format \n " , outname ) ;
}
2019-09-29 03:08:01 +00:00
2019-10-18 08:37:38 +00:00
printf ( " %s: %s %s, %i*%i, %i mips \n " , outname , imagetypename [ in - > type ] , Image_FormatName ( in - > encoding ) , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height , in - > mipcount ) ;
for ( k = 0 ; k < in - > mipcount ; k + + )
printf ( " \t %u: %i*%i*%i, %u \n " , ( unsigned ) k , in - > mip [ k ] . width , in - > mip [ k ] . height , in - > mip [ k ] . depth , ( unsigned ) in - > mip [ k ] . datasize ) ;
2019-10-14 02:36:13 +00:00
2019-10-18 08:37:38 +00:00
ImgTool_FreeMips ( in ) ;
2019-09-29 03:08:01 +00:00
}
fflush ( stdout ) ;
}
static void ImgTool_Info ( struct opts_s * args , const char * inname )
{
qbyte * indata ;
size_t fsize ;
size_t m ;
struct pendingtextureinfo * in ;
indata = FS_LoadMallocFile ( inname , & fsize ) ;
if ( ! indata )
printf ( " %s: unable to read \n " , inname ) ;
else
{
in = Image_LoadMipsFromMemory ( args - > flags , inname , inname , indata , fsize ) ;
if ( ! in )
printf ( " %s: unsupported format \n " , inname ) ;
else
{
2019-10-18 08:37:38 +00:00
printf ( " %s: %s %s, %i*%i, %i mips \n " , inname , imagetypename [ in - > type ] , Image_FormatName ( in - > encoding ) , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height , in - > mipcount ) ;
2019-09-29 03:08:01 +00:00
for ( m = 0 ; m < in - > mipcount ; m + + )
printf ( " \t %u: %i*%i*%i, %u \n " , ( unsigned ) m , in - > mip [ m ] . width , in - > mip [ m ] . height , in - > mip [ m ] . depth , ( unsigned ) in - > mip [ m ] . datasize ) ;
2019-10-14 02:36:13 +00:00
ImgTool_FreeMips ( in ) ;
2019-09-29 03:08:01 +00:00
}
}
fflush ( stdout ) ;
}
2019-10-06 01:59:13 +00:00
struct filelist_s
{
const char * * exts ;
size_t numfiles ;
struct {
char * name ;
size_t baselen ; //length up to but not including the filename extension.
} * file ;
size_t maxfiles ; //to avoid reallocs
} ;
static void FileList_Release ( struct filelist_s * list )
{
size_t i ;
for ( i = 0 ; i < list - > numfiles ; i + + )
free ( list - > file [ i ] . name ) ;
free ( list - > file ) ;
list - > numfiles = 0 ;
list - > maxfiles = 0 ;
}
static void FileList_Add ( struct filelist_s * list , char * fname )
{
size_t i ;
size_t baselen ;
const char * ext = COM_GetFileExtension ( fname , NULL ) ;
for ( i = 0 ; ; i + + )
{
if ( ! list - > exts [ i ] )
return ; //extension wasn't in the list.
if ( ! strcmp ( list - > exts [ i ] , ext ) )
break ; //one of the accepted extensions
}
baselen = ext ? ext - fname : strlen ( fname ) ;
for ( i = 0 ; i < list - > numfiles ; i + + )
{
if ( list - > file [ i ] . baselen = = baselen & & ! strncasecmp ( list - > file [ i ] . name , fname , baselen ) )
{
Con_Printf ( " Ignoring dupe file %s (using %s) \n " , fname , list - > file [ i ] . name ) ;
return ; //file already listed, but maybe with a different extension
}
}
if ( i = = list - > maxfiles )
{
list - > maxfiles + = 64 ;
list - > file = realloc ( list - > file , sizeof ( * list - > file ) * list - > maxfiles ) ;
}
list - > file [ i ] . name = strdup ( fname ) ;
list - > file [ i ] . baselen = baselen ;
list - > numfiles + + ;
}
2019-09-29 03:08:01 +00:00
# ifdef _WIN32
2019-10-06 01:59:13 +00:00
static void ImgTool_TreeScan ( struct filelist_s * list , const char * basepath , const char * subpath )
2019-09-29 03:08:01 +00:00
{
2019-10-06 01:59:13 +00:00
Con_Printf ( " ImgTool_TreeScan not implemented on windows. \n " ) ;
2019-09-29 03:08:01 +00:00
}
# else
# include <dirent.h>
# include <fnmatch.h>
2019-10-06 01:59:13 +00:00
static void ImgTool_TreeScan ( struct filelist_s * list , const char * basepath , const char * subpath )
2019-09-29 03:08:01 +00:00
{
DIR * dir ;
char file [ MAX_OSPATH ] ;
struct dirent * ent ;
2019-10-06 01:59:13 +00:00
if ( subpath & & * subpath )
Q_snprintfz ( file , sizeof ( file ) , " %s/%s " , basepath , subpath ) ;
else
Q_snprintfz ( file , sizeof ( file ) , " %s " , basepath ) ;
dir = opendir ( file ) ;
2019-09-29 03:08:01 +00:00
if ( ! dir )
{
2019-10-06 01:59:13 +00:00
Con_Printf ( " Failed to open dir %s \n " , file ) ;
2019-09-29 03:08:01 +00:00
return ;
}
for ( ; ; )
{
ent = readdir ( dir ) ;
if ( ! ent )
break ;
if ( * ent - > d_name = = ' . ' )
continue ;
else if ( ent - > d_type = = DT_DIR )
{
2019-10-06 01:59:13 +00:00
if ( ! subpath )
continue ;
if ( * subpath )
Q_snprintfz ( file , sizeof ( file ) , " %s/%s " , subpath , ent - > d_name ) ;
else
Q_snprintfz ( file , sizeof ( file ) , " %s " , ent - > d_name ) ;
ImgTool_TreeScan ( list , basepath , file ) ;
2019-09-29 03:08:01 +00:00
}
else if ( ent - > d_type = = DT_REG )
{
2019-10-06 01:59:13 +00:00
if ( subpath & & * subpath )
Q_snprintfz ( file , sizeof ( file ) , " %s/%s " , subpath , ent - > d_name ) ;
else
Q_snprintfz ( file , sizeof ( file ) , " %s " , ent - > d_name ) ;
FileList_Add ( list , file ) ;
}
}
closedir ( dir ) ;
}
# endif
static void ImgTool_TreeConvert ( struct opts_s * args , const char * srcpath , const char * destpath )
{
size_t newfiles = 0 , skippedfiles = 0 , processedfiles = 0 ;
char file [ MAX_OSPATH ] ;
char dest [ MAX_OSPATH ] ;
const char * exts [ ] = { " .png " , " .bmp " , " .tga " , " .jpg " , " .exr " , " .hdr " , NULL } ;
struct filelist_s list = { exts } ;
size_t i , destlen = strlen ( destpath ) + 1 ;
ImgTool_TreeScan ( & list , srcpath , " " ) ;
if ( ! list . numfiles )
Con_Printf ( " No suitable files found in directory: %s \n " , srcpath ) ;
for ( i = 0 ; i < list . numfiles ; i + + )
{
struct stat statsrc , statdst ;
Q_snprintfz ( file , sizeof ( file ) , " %s/%s " , srcpath , list . file [ i ] . name ) ;
Q_snprintfz ( dest , sizeof ( dest ) , " %s/%s " , destpath , list . file [ i ] . name ) ;
Q_snprintfz ( dest + destlen + list . file [ i ] . baselen , sizeof ( dest ) - destlen - list . file [ i ] . baselen , " .dds " ) ;
if ( stat ( file , & statsrc ) < 0 )
{
Con_Printf ( " stat( \" %s \" ) failed... \n " , file ) ;
continue ;
}
if ( stat ( dest , & statdst ) < 0 )
{
statdst . st_mtim . tv_sec = INT_MIN ; //make it look old
newfiles + + ;
}
if ( statdst . st_mtim . tv_sec < = statsrc . st_mtim . tv_sec )
{
processedfiles + + ;
// Con_Printf("Image file %s -> %s\n", file, dest);
FS_CreatePath ( dest , FS_SYSTEM ) ;
2019-10-18 08:37:38 +00:00
ImgTool_Convert ( args , ImgTool_Read ( args , file ) , file , dest ) ;
2019-10-06 01:59:13 +00:00
}
else
{
skippedfiles + + ;
// Con_Printf("Unmodified image file %s -> %s\n", file, dest);
}
}
Con_Printf ( " found: %u, processed: %u, skipped: %u, new: %u \n " , ( unsigned int ) list . numfiles , ( unsigned int ) processedfiles , ( unsigned int ) skippedfiles , ( unsigned int ) newfiles ) ;
FileList_Release ( & list ) ;
return ;
}
typedef struct
{
2019-10-14 02:36:13 +00:00
unsigned int offset ; // Position of the entry in WAD
unsigned int dsize ; // Size of the entry in WAD file
unsigned int size ; // Size of the entry in memory
char type ; // type of entry
char cmprs ; // Compression. 0 if none.
short dummy ; // Not used
char name [ 16 ] ; // we use only first 8
2019-10-06 01:59:13 +00:00
} wad2entry_t ;
typedef struct
{
2019-10-14 02:36:13 +00:00
char magic [ 4 ] ; //should be WAD2
unsigned int num ; //number of entries
unsigned int offset ; //location of directory
2019-10-06 01:59:13 +00:00
} wad2_t ;
2019-10-14 02:36:13 +00:00
static void ImgTool_WadConvert ( struct opts_s * args , const char * srcpath , const char * destpath , int wadtype /*x,2,3*/ )
2019-10-06 01:59:13 +00:00
{
char file [ MAX_OSPATH ] ;
2019-10-14 02:36:13 +00:00
const char * exts [ ] = { " .png " , " .bmp " , " .tga " , " .exr " , " .hdr " , " .dds " , " .ktx " , " .xcf " , " .pcx " , " .jpg " , NULL } ;
2019-10-06 01:59:13 +00:00
struct filelist_s list = { exts } ;
size_t i , u ;
vfsfile_t * f ;
char * inname ;
qbyte * indata ;
size_t fsize ;
wad2_t wad2 ;
wad2entry_t * wadentries = NULL , * entry ;
2019-10-14 02:36:13 +00:00
size_t maxentries = 0 ;
2019-10-06 01:59:13 +00:00
miptex_t mip ;
ImgTool_TreeScan ( & list , srcpath , NULL ) ;
f = FS_OpenVFS ( destpath , " wb " , FS_SYSTEM ) ;
wad2 . magic [ 0 ] = ' W ' ;
wad2 . magic [ 1 ] = ' A ' ;
wad2 . magic [ 2 ] = ' D ' ;
2019-10-14 02:36:13 +00:00
wad2 . magic [ 3 ] = ( wadtype = = 2 ) ? ' 3 ' : ' 2 ' ; //wad3 instead of 2, so we can include a palette for tools to validate against
wad2 . num = 0 ;
wad2 . offset = 0 ;
2019-10-06 01:59:13 +00:00
VFS_WRITE ( f , & wad2 , 12 ) ;
//try to decompress everything to a nice friendly palletizable range.
for ( u = 1 ; u < countof ( sh_config . texfmt ) ; u + + )
sh_config . texfmt [ u ] = ( u = = PTI_RGBA8 ) | | ( u = = PTI_RGBX8 ) | | ( u = = PTI_P8 ) ;
for ( i = 0 ; i < list . numfiles ; i + + )
{
Q_snprintfz ( file , sizeof ( file ) , " %s/%s " , srcpath , list . file [ i ] . name ) ;
inname = list . file [ i ] . name ;
if ( list . file [ i ] . baselen > 15 )
{
Con_Printf ( " Path too long for wad - %s \n " , inname ) ;
continue ;
}
indata = FS_LoadMallocFile ( file , & fsize ) ;
if ( indata )
{
2019-10-14 02:36:13 +00:00
struct pendingtextureinfo * in = Image_LoadMipsFromMemory ( args - > flags , inname , file , indata , fsize ) ;
2019-10-06 01:59:13 +00:00
Image_GenerateMips ( in , args - > flags ) ;
if ( in )
2019-09-29 03:08:01 +00:00
{
2019-10-06 01:59:13 +00:00
if ( in - > mipcount = = 1 )
Image_GenerateMips ( in , args - > flags ) ;
2019-09-29 03:08:01 +00:00
2019-10-06 01:59:13 +00:00
if ( ! in - > mipcount )
2019-09-29 03:08:01 +00:00
{
2019-10-14 02:36:13 +00:00
ImgTool_FreeMips ( in ) ;
Con_Printf ( " %s: unable to load any mips \n " , inname ) ;
2019-09-29 03:08:01 +00:00
continue ;
}
}
2019-10-14 02:36:13 +00:00
2019-10-06 01:59:13 +00:00
if ( args - > mipnum > = in - > mipcount )
{
2019-10-14 02:36:13 +00:00
ImgTool_FreeMips ( in ) ;
Con_Printf ( " %s: not enough mips \n " , inname ) ;
continue ;
}
//strip out all but the 4 mip levels we care about.
for ( u = 0 ; u < in - > mipcount ; u + + )
{
if ( u > = args - > mipnum & & u < args - > mipnum + 4 )
{
if ( ! wadtype )
{ //if we're stripping out the wad data (so that the engine ends up requiring external textures) then do it now before palettizing, for efficiency.
if ( in - > mip [ u ] . needfree )
BZ_Free ( in - > mip [ u ] . data ) ;
in - > mip [ u ] . data = NULL ;
in - > mip [ u ] . datasize = 0 ;
}
}
else
{
if ( in - > mip [ u ] . needfree )
BZ_Free ( in - > mip [ u ] . data ) ;
memset ( & in - > mip [ u ] , 0 , sizeof ( in - > mip [ u ] ) ) ;
}
}
in - > mipcount - = args - > mipnum ;
if ( in - > mipcount > 4 )
in - > mipcount = 4 ;
memmove ( & in - > mip [ 0 ] , & in - > mip [ args - > mipnum ] , sizeof ( in - > mip [ 0 ] ) * in - > mipcount ) ;
memset ( & in - > mip [ in - > mipcount ] , 0 , sizeof ( in - > mip [ 0 ] ) * ( ( args - > mipnum + 4 ) - in - > mipcount ) ) ; //null it out, just in case.
if ( ! in - > mip [ 0 ] . width | | ( in - > mip [ 0 ] . width & 15 ) )
Con_Printf ( " %s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i \n " , inname , args - > mipnum , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height ) ;
if ( ! in - > mip [ 0 ] . height | | ( in - > mip [ 0 ] . height & 15 ) )
Con_Printf ( " %s(%i): WARNING: miptex height is not a not multiple of 16 - %i*%i \n " , inname , args - > mipnum , in - > mip [ 0 ] . width , in - > mip [ 0 ] . height ) ;
if ( in - > encoding ! = PTI_P8 )
Image_ChangeFormat ( in , IF_PALETTIZE , ( * inname = = ' { ' ) ? TF_TRANS8 : PTI_INVALID , inname ) ;
if ( in - > encoding ! = PTI_P8 )
{ //erk! we failed to palettize...
ImgTool_FreeMips ( in ) ;
2019-10-06 01:59:13 +00:00
continue ;
}
if ( wad2 . num = = maxentries )
{
maxentries + = 64 ;
wadentries = realloc ( wadentries , sizeof ( * wadentries ) * maxentries ) ;
}
entry = & wadentries [ wad2 . num + + ] ;
Q_strncpyz ( entry - > name , inname , 16 ) ;
entry - > name [ list . file [ i ] . baselen ] = 0 ; //kill any .tga
2019-10-14 02:36:13 +00:00
if ( * entry - > name = = ' # ' )
* entry - > name = ' * ' ; //* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char.
2019-10-06 01:59:13 +00:00
entry - > type = TYP_MIPTEX ;
entry - > cmprs = 0 ;
entry - > dummy = 0 ;
entry - > offset = VFS_TELL ( f ) ;
memcpy ( mip . name , entry - > name , sizeof ( mip . name ) ) ;
2019-10-14 02:36:13 +00:00
mip . width = in - > mip [ 0 ] . width ;
mip . height = in - > mip [ 0 ] . height ;
mip . offsets [ 0 ] = in - > mip [ 0 ] . datasize ? sizeof ( mip ) : 0 ;
mip . offsets [ 1 ] = in - > mip [ 1 ] . datasize ? mip . offsets [ 0 ] + in - > mip [ 0 ] . datasize : 0 ;
mip . offsets [ 2 ] = in - > mip [ 2 ] . datasize ? mip . offsets [ 1 ] + in - > mip [ 1 ] . datasize : 0 ;
mip . offsets [ 3 ] = in - > mip [ 3 ] . datasize ? mip . offsets [ 2 ] + in - > mip [ 2 ] . datasize : 0 ;
Con_Printf ( " %s: %ix%i \n " , mip . name , mip . width , mip . height ) ;
2019-10-06 01:59:13 +00:00
VFS_WRITE ( f , & mip , sizeof ( mip ) ) ;
2019-10-14 02:36:13 +00:00
VFS_WRITE ( f , in - > mip [ 0 ] . data , in - > mip [ 0 ] . datasize ) ;
VFS_WRITE ( f , in - > mip [ 1 ] . data , in - > mip [ 1 ] . datasize ) ;
VFS_WRITE ( f , in - > mip [ 2 ] . data , in - > mip [ 2 ] . datasize ) ;
VFS_WRITE ( f , in - > mip [ 3 ] . data , in - > mip [ 3 ] . datasize ) ;
2019-10-06 01:59:13 +00:00
if ( wad2 . magic [ 3 ] = = ' 3 ' )
{
VFS_WRITE ( f , " \x00 \x01 " , 2 ) ;
VFS_WRITE ( f , host_basepal , 256 * 3 ) ;
}
entry - > size = entry - > dsize = VFS_TELL ( f ) - entry - > offset ;
2019-10-14 02:36:13 +00:00
ImgTool_FreeMips ( in ) ;
2019-09-29 03:08:01 +00:00
}
}
2019-10-06 01:59:13 +00:00
wad2 . offset = VFS_TELL ( f ) ;
VFS_WRITE ( f , wadentries , sizeof ( * wadentries ) * wad2 . num ) ;
VFS_SEEK ( f , 0 ) ;
VFS_WRITE ( f , & wad2 , sizeof ( wad2 ) ) ;
VFS_CLOSE ( f ) ;
FileList_Release ( & list ) ;
2019-09-29 03:08:01 +00:00
}
2019-10-14 02:36:13 +00:00
2019-09-29 03:08:01 +00:00
int main ( int argc , const char * * argv )
{
enum
{
mode_info ,
mode_convert ,
2019-10-06 01:59:13 +00:00
mode_autotree ,
2019-10-14 02:36:13 +00:00
mode_genwadx ,
mode_genwad2 ,
mode_genwad3 ,
2019-09-29 03:08:01 +00:00
} mode = mode_info ;
size_t u , f ;
2019-10-18 08:37:38 +00:00
qboolean nomoreopts = false ;
2019-09-29 03:08:01 +00:00
struct opts_s args ;
2019-10-18 08:37:38 +00:00
size_t files = 0 ;
2019-09-29 03:08:01 +00:00
for ( u = 1 ; u < countof ( sh_config . texfmt ) ; u + + )
sh_config . texfmt [ u ] = true ;
args . flags = 0 ;
args . newpixelformat = PTI_INVALID ;
args . mipnum = 0 ;
2019-10-18 08:37:38 +00:00
args . textype = - 1 ;
2019-09-29 03:08:01 +00:00
sh_config . texture2d_maxsize = 1u < < 31 ;
sh_config . texture3d_maxsize = 1u < < 31 ;
sh_config . texture2darray_maxlayers = 1u < < 31 ;
sh_config . texturecube_maxsize = 8192 ;
sh_config . texture_non_power_of_two = true ;
sh_config . texture_non_power_of_two_pic = true ;
sh_config . texture_allow_block_padding = true ;
sh_config . npot_rounddown = true ; //shouldn't be relevant
sh_config . havecubemaps = true ; //I don't think this matters.
2019-10-14 02:36:13 +00:00
ImgTool_SetupPalette ( ) ;
2019-09-29 03:08:01 +00:00
Image_Init ( ) ;
if ( argc = = 1 )
goto showhelp ;
for ( u = 1 ; u < argc ; u + + )
{
2019-10-18 08:37:38 +00:00
if ( * argv [ u ] = = ' - ' & & ! nomoreopts )
2019-09-29 03:08:01 +00:00
{
2019-10-18 08:37:38 +00:00
if ( ! strcmp ( argv [ u ] , " -- " ) )
nomoreopts = true ;
else if ( ! strcmp ( argv [ u ] , " -? " ) | | ! strcmp ( argv [ u ] , " --help " ) )
2019-09-29 03:08:01 +00:00
{
showhelp :
Con_Printf ( " show info : %s -i *.ktx \n " , argv [ 0 ] ) ;
Con_Printf ( " compress : %s --astc_6x6_ldr [--nomips] in.png out.ktx [in2.png out2.ktx] \n " , argv [ 0 ] ) ;
2019-10-06 01:59:13 +00:00
Con_Printf ( " compress : %s --bc3_rgba [--premul] [--nomips] in.png out.dds \n \t Convert pixel format (to bc3 aka dxt5) before writing to output file. \n " , argv [ 0 ] ) ;
Con_Printf ( " convert : %s --convert in.exr out.dds \n \t Convert to different file format, while trying to preserve pixel formats. \n " , argv [ 0 ] ) ;
2019-10-14 02:36:13 +00:00
Con_Printf ( " recursive : %s --astc_6x6_ldr -r srcdir destdir \n " , argv [ 0 ] ) ;
2019-10-06 01:59:13 +00:00
Con_Printf ( " decompress: %s --decompress [--exportmip 0] [--nomips] in.ktx out.png \n \t Decompresses any block-compressed pixel data. \n " , argv [ 0 ] ) ;
2019-10-14 02:36:13 +00:00
Con_Printf ( " gen wad : %s --genwad3 [--exportmip 2] srcdir out.wad \n " , argv [ 0 ] ) ;
2019-09-29 03:08:01 +00:00
Image_PrintInputFormatVersions ( ) ;
2019-10-14 02:36:13 +00:00
Con_Printf ( " Supported compressed/interesting pixelformats are: \n " ) ;
2019-09-29 03:08:01 +00:00
for ( f = 0 ; f < PTI_MAX ; f + + )
{
int bb , bw , bh ;
Image_BlockSizeForEncoding ( f , & bb , & bw , & bh ) ;
if ( f > = PTI_ASTC_FIRST & & f < = PTI_ASTC_LAST )
2019-10-14 02:36:13 +00:00
Con_Printf ( " --%-16s %5.3g-bpp (requires astcenc) \n " , Image_FormatName ( f ) , 8 * ( float ) bb / ( bw * bh ) ) ;
2019-09-29 03:08:01 +00:00
else if ( f = = PTI_BC1_RGB | | f = = PTI_BC1_RGBA | | f = = PTI_BC2_RGBA | | f = = PTI_BC3_RGBA | | f = = PTI_BC4_R8 | | f = = PTI_BC5_RG8 )
2019-10-14 02:36:13 +00:00
Con_Printf ( " --%-16s %5.3g-bpp (requires nvcompress) \n " , Image_FormatName ( f ) , 8 * ( float ) bb / ( bw * bh ) ) ;
2019-10-06 01:59:13 +00:00
else if ( f = = PTI_BC6_RGB_UFLOAT | | f = = PTI_BC6_RGB_SFLOAT | | f = = PTI_BC7_RGBA )
2019-10-14 02:36:13 +00:00
Con_Printf ( " --%-16s %5.3g-bpp (requires nvcompress 2.1+) \n " , Image_FormatName ( f ) , 8 * ( float ) bb / ( bw * bh ) ) ;
else if ( f = = PTI_RGBA16F | |
f = = PTI_RGBA32F | |
f = = PTI_E5BGR9 | |
f = = PTI_B10G11R11F | |
f = = PTI_RGB565 | |
f = = PTI_RGBA4444 | |
f = = PTI_ARGB4444 | |
f = = PTI_RGBA5551 | |
f = = PTI_ARGB1555 | |
f = = PTI_A2BGR10 | |
// f==PTI_R8 ||
// f==PTI_R16 ||
// f==PTI_R16F ||
// f==PTI_R32F ||
f = = PTI_RG8 | |
f = = PTI_L8 | |
f = = PTI_L8A8 | |
0 )
Con_Printf ( " --%-16s %5.3g-bpp \n " , Image_FormatName ( f ) , 8 * ( float ) bb / ( bw * bh ) ) ;
// else
// Con_DPrintf(" --%-16s %5.3g-bpp (unsupported)\n", Image_FormatName(f), 8*(float)bb/(bw*bh));
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
return EXIT_SUCCESS ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -c " ) | | ! strcmp ( argv [ u ] , " --convert " ) ) )
2019-09-29 03:08:01 +00:00
mode = mode_convert ;
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -d " ) | | ! strcmp ( argv [ u ] , " --decompress " ) ) )
2019-10-06 01:59:13 +00:00
{ //remove any (weird) gpu formats
2019-09-29 03:08:01 +00:00
for ( f = PTI_BC1_RGB ; f < PTI_ASTC_LAST ; f + + )
sh_config . texfmt [ f ] = false ;
mode = mode_convert ;
}
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -r " ) | | ! strcmp ( argv [ u ] , " --auto " ) ) )
2019-09-29 03:08:01 +00:00
mode = mode_autotree ;
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -i " ) | | ! strcmp ( argv [ u ] , " --info " ) ) )
2019-09-29 03:08:01 +00:00
mode = mode_info ;
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -w " ) | | ! strcmp ( argv [ u ] , " --genwad3 " ) ) )
2019-10-14 02:36:13 +00:00
mode = mode_genwad3 ;
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -w " ) | | ! strcmp ( argv [ u ] , " --genwad2 " ) ) )
2019-10-14 02:36:13 +00:00
mode = mode_genwad2 ;
2019-10-18 08:37:38 +00:00
else if ( ! files & & ( ! strcmp ( argv [ u ] , " -w " ) | | ! strcmp ( argv [ u ] , " --genwadx " ) ) )
2019-10-14 02:36:13 +00:00
mode = mode_genwadx ;
2019-10-18 08:37:38 +00:00
else if ( ! strcmp ( argv [ u ] , " --2d " ) )
args . textype = PTI_2D ;
else if ( ! strcmp ( argv [ u ] , " --3d " ) )
args . textype = PTI_3D ;
else if ( ! strcmp ( argv [ u ] , " --cube " ) )
args . textype = PTI_CUBE ;
else if ( ! strcmp ( argv [ u ] , " --2darray " ) )
args . textype = PTI_2D_ARRAY ;
else if ( ! strcmp ( argv [ u ] , " --cubearray " ) )
args . textype = PTI_CUBE_ARRAY ;
2019-09-29 03:08:01 +00:00
else if ( ! strcmp ( argv [ u ] , " --nomips " ) )
args . flags | = IF_NOMIPMAP ;
else if ( ! strcmp ( argv [ u ] , " --mips " ) )
args . flags & = ~ IF_NOMIPMAP ;
2019-10-06 01:59:13 +00:00
else if ( ! strcmp ( argv [ u ] , " --premul " ) )
args . flags | = IF_PREMULTIPLYALPHA ;
else if ( ! strcmp ( argv [ u ] , " --nopremul " ) )
args . flags & = ~ IF_PREMULTIPLYALPHA ;
2019-09-29 03:08:01 +00:00
else if ( ! strcmp ( argv [ u ] , " --exportmip " ) )
{
char * e = " erk " ;
if ( u + 1 < argc )
args . mipnum = strtoul ( argv [ + + u ] , & e , 10 ) ;
if ( * e )
{
Con_Printf ( " --exportmip requires trailing numeric argument \n " ) ;
return 1 ;
}
}
else
{
if ( argv [ u ] [ 1 ] = = ' - ' )
{
for ( f = 0 ; f < PTI_MAX ; f + + )
{
if ( ! strcasecmp ( argv [ u ] + 2 , Image_FormatName ( f ) ) )
{
args . newpixelformat = f ;
mode = mode_convert ;
break ;
}
}
if ( f < PTI_MAX )
continue ;
}
Con_Printf ( " Unknown arg %s \n " , argv [ u ] ) ;
goto showhelp ;
}
2019-10-18 08:37:38 +00:00
argv [ u ] = NULL ;
2019-09-29 03:08:01 +00:00
}
else
2019-10-18 08:37:38 +00:00
argv [ files + + ] = argv [ u ] ;
2019-09-29 03:08:01 +00:00
}
2019-10-18 08:37:38 +00:00
if ( mode = = mode_info )
{ //just print info about each listed file.
for ( u = 0 ; u < files ; u + + )
ImgTool_Info ( & args , argv [ u ] ) ;
}
else if ( mode = = mode_convert & & files > 1 & & args . textype > = 0 ) //overwrite input
{
files - - ;
ImgTool_Convert ( & args , ImgTool_Combine ( & args , argv , files ) , " combined " , argv [ files ] ) ;
}
else if ( mode = = mode_convert & & files = = 1 & & args . textype < 0 ) //overwrite input
ImgTool_Convert ( & args , ImgTool_Read ( & args , argv [ u ] ) , argv [ u ] , argv [ u ] ) ;
else if ( mode = = mode_convert & & ! ( files & 1 ) & & args . textype < 0 ) //list of pairs
{
//-c src1 dst1 src2 dst2
for ( u = 0 ; u + 1 < files ; u + = 2 )
ImgTool_Convert ( & args , ImgTool_Read ( & args , argv [ u ] ) , argv [ u ] , argv [ u + 1 ] ) ;
}
else if ( mode = = mode_autotree & & files = = 2 )
ImgTool_TreeConvert ( & args , argv [ 0 ] , argv [ 1 ] ) ;
else if ( ( mode = = mode_genwad2 | | mode = = mode_genwad3 | | mode = = mode_genwadx ) & & files = = 2 )
ImgTool_WadConvert ( & args , argv [ 0 ] , argv [ 1 ] , mode - mode_genwadx ) ;
else
return EXIT_FAILURE ;
return EXIT_SUCCESS ;
2019-09-29 03:08:01 +00:00
}