| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** 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.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- // FILE: textureCompress.cpp //////////////////////////////////////////////////////
- // Author: Matthew D. Campbell, Dec 2002
- // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
- #define WIN32_LEAN_AND_MEAN // only bare bones windows stuff wanted
- //#include <afxwin.h>
- #include <windows.h>
- #include <lmcons.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include "resource.h"
- #include <map>
- #include <string>
- #include <set>
- #include <cstdarg>
- #include <io.h>
- #include <sys/stat.h>
- #include <sys/utime.h>
- static const char *nodxtPrefix[] = {
- "zhca",
- "caust",
- NULL,
- };
- static const char *nodxtAnywhere[] = {
- "userinterface",
- "controlbar",
- "commandbar",
- NULL,
- };
- #define LOG(x) logStuff x
- static void logStuff(const char *fmt, ...)
- {
- static char buffer[1024];
- va_list va;
- va_start( va, fmt );
- _vsnprintf(buffer, 1024, fmt, va );
- buffer[1023] = 0;
- va_end( va );
- puts(buffer);
- ::MessageBox(NULL, buffer, "textureCompress", MB_OK);
- }
- #ifndef NDEBUG
- class DebugMunkee
- {
- public:
- DebugMunkee(const char *fname = "debugLog.txt") { m_fp = fopen(fname, "w"); }
- ~DebugMunkee() { if (m_fp) fclose(m_fp); m_fp = NULL; }
- FILE *m_fp;
- };
- static DebugMunkee *theDebugMunkee = NULL;
- #define DEBUG_LOG(x) debugLog x
- static void debugLog(const char *fmt, ...)
- {
- static char buffer[1024];
- va_list va;
- va_start( va, fmt );
- _vsnprintf(buffer, 1024, fmt, va );
- buffer[1023] = 0;
- va_end( va );
- OutputDebugString( buffer );
- puts(buffer);
- if (theDebugMunkee)
- fputs(buffer, theDebugMunkee->m_fp);
- }
- #else
- #define DEBUG_LOG(x) {}
- #endif // NDEBUG
- static void usage(const char *progname)
- {
- if (!progname)
- progname = "textureCompress";
- LOG (("Usage: %s sourceDir destDir cacheDir outFile dxtOutFile\n", progname));
- }
- class FileInfo
- {
- public:
- FileInfo() {}
- ~FileInfo() {}
- void set( const WIN32_FIND_DATA& info );
- std::string filename;
- time_t creationTime;
- time_t accessTime;
- time_t modTime;
- DWORD attributes;
- DWORD filesize; // only care about 32 bits for our purposes
- protected:
- };
- struct FileInfoComparator
- {
- bool operator()(const FileInfo& a, const FileInfo& b) const
- {
- return a.filename < b.filename;
- }
- };
- //-------------------------------------------------------------------------------------------------
- typedef std::set<FileInfo, FileInfoComparator> FileInfoSet;
- //-------------------------------------------------------------------------------------------------
- class Directory
- {
- public:
- Directory(const std::string& dirPath);
- ~Directory() {}
- FileInfoSet* getFiles( void );
- FileInfoSet* getSubdirs( void );
- protected:
- std::string m_dirPath;
- FileInfoSet m_files;
- FileInfoSet m_subdirs;
- };
- //-------------------------------------------------------------------------------------------------
- static void TimetToFileTime( time_t t, FILETIME& ft )
- {
- LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
- ft.dwLowDateTime = (DWORD) ll;
- ft.dwHighDateTime = ll >>32;
- }
- static time_t FileTimeToTimet( const FILETIME& ft )
- {
- LONGLONG ll = (ft.dwHighDateTime << 32) + ft.dwLowDateTime - 116444736000000000;
- ll /= 10000000;
- return (time_t)ll;
- }
- //-------------------------------------------------------------------------------------------------
- void FileInfo::set( const WIN32_FIND_DATA& info )
- {
- filename = info.cFileName;
- for (int i=0; i<filename.size(); ++i)
- {
- char c[2] = { tolower(info.cFileName[i]), 0 };
- filename.replace(i, 1, c, 1);
- }
- creationTime = FileTimeToTimet(info.ftCreationTime);
- accessTime = FileTimeToTimet(info.ftLastAccessTime);
- modTime = FileTimeToTimet(info.ftLastWriteTime);
- attributes = info.dwFileAttributes;
- filesize = info.nFileSizeLow;
- struct stat origStat;
- stat( filename.c_str(), &origStat);
- modTime = origStat.st_mtime; // use stat(), since the LONGLONG code is unpredictable
- //DEBUG_LOG(("FileInfo::set(): fname=%s, size=%d, modTime=%d\n", filename.c_str(), filesize, modTime));
- }
- //-------------------------------------------------------------------------------------------------
- Directory::Directory( const std::string& dirPath ) : m_dirPath(dirPath)
- {
- WIN32_FIND_DATA item; // search item
- HANDLE hFile; // handle for search resources
- char currDir[ MAX_PATH ];
- // sanity
- if( !m_dirPath.length() )
- {
- return;
- }
- // save the current directory
- GetCurrentDirectory( MAX_PATH, currDir );
- // switch into the directory provided
- if( SetCurrentDirectory( m_dirPath.c_str() ) == 0 )
- {
- return;
- }
- // go through each item in the output directory
- bool done = false;
- hFile = FindFirstFile( "*", &item);
- if( hFile == INVALID_HANDLE_VALUE )
- {
- done = true;
- }
- FileInfo info;
- while (!done)
- {
- // if this is a subdirectory keep the name around till the end
- if( item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
- {
- if ( strcmp( item.cFileName, "." ) && strcmp( item.cFileName, ".." ) )
- {
- info.set(item);
- m_subdirs.insert( info );
- }
- }
- else
- {
- info.set(item);
- m_files.insert( info );
- }
- if ( FindNextFile( hFile, &item ) == 0 )
- {
- done = true;
- }
- }
- // close search
- FindClose( hFile );
- // restore the working directory to what it was when we started here
- SetCurrentDirectory( currDir );
- }
- FileInfoSet* Directory::getFiles( void )
- {
- return &m_files;
- }
- FileInfoSet* Directory::getSubdirs( void )
- {
- return &m_subdirs;
- }
- //-------------------------------------------------------------------------------------------------
- // strtrim ====================================================================
- /** Trim leading and trailing whitespace from a character string (in place). */
- //=============================================================================
- static char* strtrim(char* buffer)
- {
- if (buffer != NULL) {
- // Strip leading white space from the string.
- char * source = buffer;
- while ((*source != 0) && ((unsigned char)*source <= 32))
- {
- source++;
- }
- if (source != buffer)
- {
- strcpy(buffer, source);
- }
- // Clip trailing white space from the string.
- for (int index = strlen(buffer)-1; index >= 0; index--)
- {
- if ((*source != 0) && ((unsigned char)buffer[index] <= 32))
- {
- buffer[index] = '\0';
- }
- else
- {
- break;
- }
- }
- }
- return buffer;
- }
- //-------------------------------------------------------------------------------------------------
- typedef std::set<std::string> StringSet;
- //-------------------------------------------------------------------------------------------------
- void eraseCachedFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
- StringSet& cachedFilesToErase)
- {
- StringSet::const_iterator sit;
- for (sit = cachedFilesToErase.begin(); sit != cachedFilesToErase.end(); ++sit)
- {
- std::string src = cacheDirName;
- src.append("\\");
- src.append(*sit);
- DEBUG_LOG(("Erasing cached file: %s\n", src.c_str()));
- DeleteFile(src.c_str());
- }
- }
- //-------------------------------------------------------------------------------------------------
- void copyCachedFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
- StringSet& cachedFilesToCopy)
- {
- StringSet::const_iterator sit;
- for (sit = cachedFilesToCopy.begin(); sit != cachedFilesToCopy.end(); ++sit)
- {
- std::string src = cacheDirName;
- src.append("\\");
- src.append(*sit);
- std::string dest = targetDirName;
- dest.append("\\");
- dest.append(*sit);
- DEBUG_LOG(("Copying cached file: %s\n", src.c_str()));
- if (_chmod(dest.c_str(), _S_IWRITE | _S_IREAD) == -1)
- {
- DEBUG_LOG(("Cannot chmod '%s'\n", dest.c_str()));
- }
- CopyFile(src.c_str(), dest.c_str(), FALSE);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void compressOrigFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
- StringSet& origFilesToCompress, const std::string& dxtOutFname)
- {
- char tmpPath[_MAX_PATH] = "C:\\temp\\";
- char tmpFname[_MAX_PATH] = "C:\\temp\\tmp.txt";
- GetTempPath(_MAX_PATH, tmpPath);
- GetTempFileName(tmpPath, "tex", 0, tmpFname);
- HANDLE h = CreateFile(tmpFname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
- if (!h)
- {
- DEBUG_LOG(("Could not create temp file '%s'! Unable to compress textures!\n", tmpFname));
- }
- StringSet::const_iterator sit;
- for (sit = origFilesToCompress.begin(); sit != origFilesToCompress.end(); ++sit)
- {
- std::string tmp = sourceDirName;
- tmp.append("\\");
- tmp.append(*sit);
- tmp.append("\n");
- DEBUG_LOG(("Compressing file: %s", tmp.c_str()));
- DWORD len;
- WriteFile(h, tmp.c_str(), tmp.length(), &len, NULL);
- }
- CloseHandle(h);
- std::string commandLine;
- commandLine = "\\projects\\rts\\build\\nvdxt -list ";
- commandLine.append(tmpFname);
- commandLine.append(" -24 dxt1c -32 dxt5 -full -outdir ");
- commandLine.append(cacheDirName);
- commandLine.append(" > ");
- commandLine.append(dxtOutFname);
- DEBUG_LOG(("Compressing textures with command line of '%s'\n", commandLine.c_str()));
- int ret = system(commandLine.c_str());
- DEBUG_LOG(("system(%s) returned %d\n", commandLine.c_str(), ret));
- DeleteFile(tmpFname);
- // now copy compressed file to target dir
- for (sit = origFilesToCompress.begin(); sit != origFilesToCompress.end(); ++sit)
- {
- std::string orig = sourceDirName;
- orig.append("\\");
- orig.append(*sit);
- struct stat origStat;
- stat( orig.c_str(), &origStat);
- struct _utimbuf utb;
- utb.actime = origStat.st_atime;
- utb.modtime = origStat.st_mtime;
- std::string src = cacheDirName;
- src.append("\\");
- src.append(*sit);
- src.replace(src.size()-4, 4, ".dds");
- _utime(src.c_str(), &utb);
- std::string dest = targetDirName;
- dest.append("\\");
- dest.append(*sit);
- dest.replace(dest.size()-4, 4, ".dds");
- DEBUG_LOG(("Copying new file from %s to %s\n", src.c_str(), dest.c_str()));
- if (_chmod(dest.c_str(), _S_IWRITE | _S_IREAD) == -1)
- {
- DEBUG_LOG(("Cannot chmod '%s'\n", dest.c_str()));
- }
- BOOL ret = CopyFile(src.c_str(), dest.c_str(), FALSE);
- if (!ret)
- {
- DEBUG_LOG(("Could not copy file!\n"));
- }
- _utime(dest.c_str(), &utb);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void copyOrigFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
- StringSet& origFilesToCopy)
- {
- StringSet::const_iterator sit;
- for (sit = origFilesToCopy.begin(); sit != origFilesToCopy.end(); ++sit)
- {
- std::string src = sourceDirName;
- src.append("\\");
- src.append(*sit);
- std::string dest = targetDirName;
- dest.append("\\");
- dest.append(*sit);
- if (_chmod(dest.c_str(), _S_IWRITE | _S_IREAD) == -1)
- {
- DEBUG_LOG(("Cannot chmod '%s'\n", dest.c_str()));
- }
- BOOL res = CopyFile(src.c_str(), dest.c_str(), FALSE);
- DEBUG_LOG(("Copying file: %s returns %d\n", src.c_str(), res));
- }
- }
- //-------------------------------------------------------------------------------------------------
- static void scanDir( const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName, const std::string& dxtOutFname )
- {
- DEBUG_LOG(("Scanning '%s'\n", sourceDirName.c_str()));
- Directory sourceDir(sourceDirName);
- DEBUG_LOG(("Scanning '%s'\n", targetDirName.c_str()));
- Directory targetDir(targetDirName);
- DEBUG_LOG(("Scanning '%s'\n", cacheDirName.c_str()));
- Directory cacheDir(cacheDirName);
- FileInfoSet *sourceFiles = sourceDir.getFiles();
- FileInfoSet *cacheFiles = cacheDir.getFiles();
- FileInfoSet *targetFiles = targetDir.getFiles();
- StringSet cachedFilesToErase;
- StringSet cachedFilesToCopy;
- StringSet origFilesToCompress;
- StringSet origFilesToCopy;
- DEBUG_LOG(("Emptying targetDir\n"));
- for (FileInfoSet::iterator targetIt = targetFiles->begin(); targetIt != targetFiles->end(); ++targetIt)
- {
- FileInfo f = *targetIt;
- std::string fname = f.filename;
- f.filename.replace(f.filename.size()-4, 4, ".tga");
- FileInfoSet::iterator fit = sourceFiles->find(f);
- if (fit == sourceFiles->end())
- {
- // look for pre-existing dds files too
- f.filename.replace(f.filename.size()-4, 4, ".dds");
- FileInfoSet::iterator ddsfit = sourceFiles->find(f);
- if (ddsfit == sourceFiles->end())
- {
- fname.insert(0, "\\");
- fname.insert(0, targetDirName);
- DEBUG_LOG(("Deleting now-removed file '%s'\n", fname.c_str()));
- DeleteFile(fname.c_str());
- }
- }
- }
- for (FileInfoSet::iterator cacheIt = cacheFiles->begin(); cacheIt != cacheFiles->end(); ++cacheIt)
- {
- FileInfo f = *cacheIt;
- int len = f.filename.size();
- if (len < 5)
- {
- cachedFilesToErase.insert(f.filename);
- continue;
- }
- std::string fname = f.filename;
- f.filename.replace(len-4, 4, ".tga");
- FileInfoSet::iterator fit = sourceFiles->find(f);
- if (fit != sourceFiles->end())
- {
- FileInfo sf = *fit;
- if (f.modTime < sf.modTime)
- {
- /**
- std::string orig = sourceDirName;
- orig.append("\\");
- orig.append(sf.filename);
- struct stat origStat;
- stat( orig.c_str(), &origStat);
- struct _utimbuf utb;
- utb.actime = origStat.st_atime;
- utb.modtime = origStat.st_mtime;
- std::string dest = cacheDirName;
- dest.append("\\");
- dest.append(f.filename);
- dest.replace(dest.size()-4, 4, ".dds");
- _utime(dest.c_str(), &utb);
- cachedFilesToCopy.insert(fname);
- /**/
- cachedFilesToErase.insert(fname);
- }
- else
- {
- f.filename = fname; // back to .dds
- FileInfoSet::iterator it = targetFiles->find(f);
- if (it == targetFiles->end())
- cachedFilesToCopy.insert(fname);
- }
- }
- else
- {
- cachedFilesToErase.insert(fname);
- }
- }
- for (FileInfoSet::iterator sourceIt = sourceFiles->begin(); sourceIt != sourceFiles->end(); ++sourceIt)
- {
- FileInfo f = *sourceIt;
- std::string fname = f.filename;
- const char *s = fname.c_str();
- int index = 0;
- const char *check = nodxtPrefix[0];
- bool shouldSkip = false;
- while (check)
- {
- if (fname.find(check) == 0)
- {
- shouldSkip = true;
- break;
- }
- check = nodxtPrefix[++index];
- }
- index = 0;
- check = nodxtAnywhere[0];
- while (check && !shouldSkip)
- {
- if (fname.find(check) != fname.npos)
- {
- shouldSkip = true;
- break;
- }
- check = nodxtAnywhere[++index];
- }
- if (!shouldSkip)
- {
- // check for preexisting .dds files so we can just copy them
- if (fname.find(".dds") != fname.npos)
- {
- shouldSkip = true;
- }
- }
- if (shouldSkip)
- {
- origFilesToCopy.insert(s);
- }
- else
- {
- int len = f.filename.size();
- f.filename.replace(len-4, 4, ".dds");
- FileInfoSet::iterator fit = cacheFiles->find(f);
- if (fit != cacheFiles->end())
- {
- FileInfo cf = *fit;
- if (cf.modTime < f.modTime)
- {
- origFilesToCompress.insert(fname);
- }
- }
- else
- {
- origFilesToCompress.insert(fname);
- }
- }
- }
- // now dump our files
- eraseCachedFiles (sourceDirName, targetDirName, cacheDirName, cachedFilesToErase);
- copyCachedFiles (sourceDirName, targetDirName, cacheDirName, cachedFilesToCopy);
- copyOrigFiles (sourceDirName, targetDirName, cacheDirName, origFilesToCopy);
- compressOrigFiles(sourceDirName, targetDirName, cacheDirName, origFilesToCompress, dxtOutFname);
- }
- //-------------------------------------------------------------------------------------------------
- #define USE_WINMAIN
- #ifdef USE_WINMAIN
- int APIENTRY WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow)
- {
- /*
- ** Convert WinMain arguments to simple main argc and argv
- */
- int argc = 1;
- char * argv[20];
- argv[0] = NULL;
- char * token = strtok(lpCmdLine, " ");
- while (argc < 20 && token != NULL)
- {
- argv[argc++] = strtrim(token);
- token = strtok(NULL, " ");
- }
- #else
- int main(int argc, const char **argv)
- {
- #endif // USE_WINMAIN
- if (argc != 6)
- {
- usage(argv[0]);
- }
- else
- {
- const char *sourceDir = argv[1];
- const char *targetDir = argv[2];
- const char *cacheDir = argv[3];
- #ifndef NDEBUG
- theDebugMunkee = new DebugMunkee(argv[4]);
- #endif
- //setUpLoadWindow();
- scanDir(sourceDir, targetDir, cacheDir, argv[5]);
- //setLoadWindowText("Writing to file...");
- //printSet( noAlphaChannel, "No Alpha Channel" );
- //printSet( noAlpha, "Not Using Alpha Channel" );
- //printSet( hasAlpha, "Using Alpha Channel" );
- //tearDownLoadWindow();
- #ifndef NDEBUG
- delete theDebugMunkee;
- theDebugMunkee = NULL;
- #endif
- }
- return 0;
- }
|