#include "quakedef.h" #include "shader.h" #undef stderr #define stderr stdout #include #ifdef _WIN32 #include #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 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); } qboolean FS_Remove (const char *path, enum fs_relative relativeto) { //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 #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 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); } 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; 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); } qbyte *host_basepal; unsigned int d_8to24rgbtable[256]; unsigned int d_8to24bgrtable[256]; qbyte GetPaletteIndexNoFB(int red, int green, int blue) { return 0; } sh_config_t sh_config; viddef_t vid; struct opts_s { 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); static enum uploadfmt ImgTool_ASTCToLDR(uploadfmt_t fmt) { 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; } if (fmt == PTI_BC1_RGB) return PTI_BC1_RGBA; return fmt; } #ifdef _WIN32 #include 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 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 static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, struct pendingtextureinfo *mips) { 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; //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; 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"); 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"); else return false; 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)); 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. Q_strncatz(command, ">> /dev/null", sizeof(command)); Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); for (m = 0; m < mips->mipcount; m++) { tmp.mip[0] = mips->mip[m]; tmp.mip[0].needfree = false; (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, &mips->mip[m].data, 1, mips->mip[m].width*bb, mips->mip[m].width, mips->mip[m].height, mips->encoding, false)) #endif break; } system(command); 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 == mips->mip[m].depth && ImgTool_ASTCToLDR(ret->encoding) == ImgTool_ASTCToLDR(targfmt)) { mips->mip[m] = ret->mip[0]; continue; } break; } 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) 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); } FS_Remove(raw, FS_SYSTEM); FS_Remove(comp, FS_SYSTEM); return true; } 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 ""; } static void ImgTool_Convert(struct opts_s *args, const char *inname, const char *outname) { qbyte *indata; size_t fsize; struct pendingtextureinfo *in; const char *outext = COM_GetFileExtension(outname, NULL); qboolean allowcompressed = false; if (!strcmp(outext, ".dds") || !strcmp(outext, ".ktx")) allowcompressed = true; indata = FS_LoadMallocFile(inname, &fsize); if (indata) { in = Image_LoadMipsFromMemory(args->flags|(allowcompressed?0:IF_NOMIPMAP), inname, inname, indata, fsize); if (in) { printf("%s: %s, %i*%i, %i mips\n", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); /*if (imgtool_convertto >= PTI_BC1_RGB && imgtool_convertto <= PTI_BC5_RG8_SNORM) ImgTool_ConvertToBCn(imgtool_convertto, in); else*/ { if (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1) Image_GenerateMips(in, args->flags); if (args->newpixelformat != PTI_INVALID && allowcompressed && ImgTool_ConvertPixelFormat(args, inname, in)) printf("\t(Converted to %s)\n", Image_FormatName(in->encoding)); } if (!in->mipcount) { printf("%s: unable to convert any mips\n", inname); return; } if (0) ; #ifdef IMAGEFMT_KTX else if (!strcmp(outext, ".ktx")) Image_WriteKTXFile(outname, FS_SYSTEM, in); #endif #ifdef IMAGEFMT_DDS else if (!strcmp(outext, ".dds")) Image_WriteDDSFile(outname, FS_SYSTEM, in); #endif else { int bb,bw,bh; Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); if (args->mipnum < in->mipcount) { if (0) ; #ifdef IMAGEFMT_PNG else if (!strcmp(outext, ".png")) { #ifdef AVAIL_PNGLIB if (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[args->mipnum].data, 1, in->mip[args->mipnum].width*bb, in->mip[args->mipnum].width, in->mip[args->mipnum].height, in->encoding, false)) #endif Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } #endif #ifdef IMAGEFMT_TGA else if (!strcmp(outext, ".tga")) { if (!WriteTGA(outname, FS_SYSTEM, in->mip[args->mipnum].data, in->mip[args->mipnum].width*bb, in->mip[args->mipnum].width, in->mip[args->mipnum].height, in->encoding)) Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } #endif else Con_Printf("%s: Unknown output file format\n", outname); } else Con_Printf("%s: Requested output mip number was out of bounds %i >= %i\n", outname, args->mipnum, in->mipcount); } // printf("%s: %s, %i*%i, %i mips\n", outname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); // 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); } else printf("%s: unsupported format\n", inname); } else printf("%s: unable to read\n", inname); 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 { printf("%s: %s, %i*%i, %i mips\n", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); 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); } } fflush(stdout); } 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++; } #ifdef _WIN32 static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, const char *subpath) { Con_Printf("ImgTool_TreeScan not implemented on windows.\n"); } #else #include #include static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, const char *subpath) { DIR *dir; char file[MAX_OSPATH]; struct dirent *ent; if (subpath && *subpath) Q_snprintfz(file, sizeof(file), "%s/%s", basepath, subpath); else Q_snprintfz(file, sizeof(file), "%s", basepath); dir = opendir(file); if (!dir) { Con_Printf("Failed to open dir %s\n", file); return; } for (;;) { ent = readdir(dir); if (!ent) break; if (*ent->d_name == '.') continue; else if (ent->d_type == DT_DIR) { 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); } else if (ent->d_type == DT_REG) { 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); ImgTool_Convert(args, file, dest); } 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 { long offset; // Position of the entry in WAD long dsize; // Size of the entry in WAD file long 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 } wad2entry_t; typedef struct { char magic[4]; //should be WAD2 long num; //number of entries long offset; //location of directory } wad2_t; static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const char *destpath) { char file[MAX_OSPATH]; const char *exts[] = {".png", ".bmp", ".tga", ".jpg", ".exr", ".hdr", NULL}; 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; size_t maxentries; 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'; wad2.magic[3] = '3'; //wad3 instead of 2, so we can include a palette for tools to validate against 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) { struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags|IF_PALETTIZE, inname, file, indata, fsize); Image_GenerateMips(in, args->flags); if (in) { if (in->mipcount == 1) Image_GenerateMips(in, args->flags); if (!in->mipcount) { printf("%s: unable to load any mips\n", inname); continue; } } if (args->mipnum >= in->mipcount) { printf("%s: not enough mips\n", inname); continue; } if ((in->mip[args->mipnum].width|in->mip[args->mipnum].height) & 15) printf("%s(%i): WARNING: not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[args->mipnum].width, in->mip[args->mipnum].height); 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 entry->type = TYP_MIPTEX; entry->cmprs = 0; entry->dummy = 0; entry->offset = VFS_TELL(f); memcpy(mip.name, entry->name, sizeof(mip.name)); mip.width = in->mip[args->mipnum].width; mip.height = in->mip[args->mipnum].height; mip.offsets[0] = in->mip[args->mipnum+0].datasize?sizeof(mip):0; mip.offsets[1] = in->mip[args->mipnum+1].datasize?mip.offsets[args->mipnum+0]+in->mip[args->mipnum+0].datasize:0; mip.offsets[2] = in->mip[args->mipnum+2].datasize?mip.offsets[args->mipnum+1]+in->mip[args->mipnum+1].datasize:0; mip.offsets[3] = in->mip[args->mipnum+3].datasize?mip.offsets[args->mipnum+2]+in->mip[args->mipnum+2].datasize:0; VFS_WRITE(f, &mip, sizeof(mip)); VFS_WRITE(f, in->mip[args->mipnum+0].data, in->mip[args->mipnum+0].datasize); VFS_WRITE(f, in->mip[args->mipnum+1].data, in->mip[args->mipnum+1].datasize); VFS_WRITE(f, in->mip[args->mipnum+2].data, in->mip[args->mipnum+2].datasize); VFS_WRITE(f, in->mip[args->mipnum+3].data, in->mip[args->mipnum+3].datasize); 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; } } 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); } */ int main(int argc, const char **argv) { enum { mode_info, mode_convert, mode_autotree, mode_genwad } mode = mode_info; size_t u, f; struct opts_s args; for (u = 1; u < countof(sh_config.texfmt); u++) sh_config.texfmt[u] = true; args.flags = 0; args.newpixelformat = PTI_INVALID; args.mipnum = 0; 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. Image_Init(); if (argc==1) goto showhelp; for (u = 1; u < argc; u++) { if (*argv[u] == '-') { if (!strcmp(argv[u], "-?") || !strcmp(argv[u], "--help")) { 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]); Con_Printf("compress : %s --bc3_rgba [--premul] [--nomips] in.png out.dds\n\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\n", argv[0]); Con_Printf("convert : %s --convert in.exr out.dds\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]); Con_Printf("decompress: %s --decompress [--exportmip 0] [--nomips] in.ktx out.png\n\tDecompresses any block-compressed pixel data.\n", argv[0]); // Con_Printf("gen wad : %s --genwad [--exportmip 2] srcdir out.wad\n", argv[0]); // Con_Printf("auto : %s --astc_6x6_ldr -r _postfix.png srcdir destdir\n", argv[0]); Image_PrintInputFormatVersions(); Con_Printf("Supported compressed pixelformats are:\n"); 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) Con_Printf(" --%-15s %.2fbpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); 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) Con_Printf(" --%-15s %.2fbpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); else if (f==PTI_BC6_RGB_UFLOAT || f==PTI_BC6_RGB_SFLOAT || f==PTI_BC7_RGBA) Con_Printf(" --%-15s %.2fbpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); } break; } else if (!strcmp(argv[u], "-c") || !strcmp(argv[u], "--convert")) mode = mode_convert; else if (!strcmp(argv[u], "-d") || !strcmp(argv[u], "--decompress")) { //remove any (weird) gpu formats for (f = PTI_BC1_RGB; f < PTI_ASTC_LAST; f++) sh_config.texfmt[f] = false; mode = mode_convert; } else if (!strcmp(argv[u], "-r") || !strcmp(argv[u], "--auto")) mode = mode_autotree; else if (!strcmp(argv[u], "-i") || !strcmp(argv[u], "--info")) mode = mode_info; else if (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad")) mode = mode_genwad; else if (!strcmp(argv[u], "--nomips") ) args.flags |= IF_NOMIPMAP; else if (!strcmp(argv[u], "--mips")) args.flags &= ~IF_NOMIPMAP; else if (!strcmp(argv[u], "--premul") ) args.flags |= IF_PREMULTIPLYALPHA; else if (!strcmp(argv[u], "--nopremul")) args.flags &= ~IF_PREMULTIPLYALPHA; 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; } } else { if (mode == mode_info) ImgTool_Info(&args, argv[u]); else if (mode == mode_convert) { if (u+1 < argc) { ImgTool_Convert(&args, argv[u], argv[u+1]); u++; } } else if (mode == mode_autotree) { if (u+1 < argc) { ImgTool_TreeConvert(&args, argv[u], argv[u+1]); u++; } } else if (mode == mode_genwad) { if (u+1 < argc) { //ImgTool_WadConvert(&args, argv[u], argv[u+1]); u++; } } } } return 0; }