Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
textureCompress.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19// FILE: textureCompress.cpp //////////////////////////////////////////////////////
20// Author: Matthew D. Campbell, Dec 2002
21
22// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
23#define WIN32_LEAN_AND_MEAN // only bare bones windows stuff wanted
24//#include <afxwin.h>
25#include <windows.h>
26#include <lmcons.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30
31#include "resource.h"
32#include <map>
33#include <string>
34#include <set>
35#include <cstdarg>
36#include <io.h>
37#include <sys/stat.h>
38#include <sys/utime.h>
39
40static const char *nodxtPrefix[] = {
41 "zhca",
42 "caust",
43 NULL,
44};
45
46static const char *nodxtAnywhere[] = {
47 "userinterface",
48 "controlbar",
49 "commandbar",
50 NULL,
51};
52
53#define LOG(x) logStuff x
54static void logStuff(const char *fmt, ...)
55{
56 static char buffer[1024];
57 va_list va;
58 va_start( va, fmt );
59 _vsnprintf(buffer, 1024, fmt, va );
60 buffer[1023] = 0;
61 va_end( va );
62
63 puts(buffer);
64 ::MessageBox(NULL, buffer, "textureCompress", MB_OK);
65}
66
67#ifndef NDEBUG
68
70{
71public:
72 DebugMunkee(const char *fname = "debugLog.txt") { m_fp = fopen(fname, "w"); }
73 ~DebugMunkee() { if (m_fp) fclose(m_fp); m_fp = NULL; }
74
75 FILE *m_fp;
76};
77
78static DebugMunkee *theDebugMunkee = NULL;
79
80#define DEBUG_LOG(x) debugLog x
81static void debugLog(const char *fmt, ...)
82{
83 static char buffer[1024];
84 va_list va;
85 va_start( va, fmt );
86 _vsnprintf(buffer, 1024, fmt, va );
87 buffer[1023] = 0;
88 va_end( va );
89
90 OutputDebugString( buffer );
91 puts(buffer);
92 if (theDebugMunkee)
93 fputs(buffer, theDebugMunkee->m_fp);
94}
95
96#else
97
98#define DEBUG_LOG(x) {}
99
100#endif // NDEBUG
101
102
103static void usage(const char *progname)
104{
105 if (!progname)
106 progname = "textureCompress";
107 LOG (("Usage: %s sourceDir destDir cacheDir outFile dxtOutFile\n", progname));
108}
109
110class FileInfo
111{
112public:
115
116 void set( const WIN32_FIND_DATA& info );
117
118 std::string filename;
123 DWORD filesize; // only care about 32 bits for our purposes
124
125protected:
126};
127
129{
130 bool operator()(const FileInfo& a, const FileInfo& b) const
131 {
132 return a.filename < b.filename;
133 }
134};
135
136//-------------------------------------------------------------------------------------------------
137
138typedef std::set<FileInfo, FileInfoComparator> FileInfoSet;
139
140//-------------------------------------------------------------------------------------------------
141
143{
144public:
145 Directory(const std::string& dirPath);
147
148 FileInfoSet* getFiles( void );
149 FileInfoSet* getSubdirs( void );
150
151protected:
152 std::string m_dirPath;
153
156};
157
158//-------------------------------------------------------------------------------------------------
159
160static void TimetToFileTime( time_t t, FILETIME& ft )
161{
162 LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
163 ft.dwLowDateTime = (DWORD) ll;
164 ft.dwHighDateTime = ll >>32;
165}
166
167static time_t FileTimeToTimet( const FILETIME& ft )
168{
169 LONGLONG ll = (ft.dwHighDateTime << 32) + ft.dwLowDateTime - 116444736000000000;
170 ll /= 10000000;
171 return (time_t)ll;
172}
173
174//-------------------------------------------------------------------------------------------------
175
176void FileInfo::set( const WIN32_FIND_DATA& info )
177{
178 filename = info.cFileName;
179 for (int i=0; i<filename.size(); ++i)
180 {
181 char c[2] = { tolower(info.cFileName[i]), 0 };
182 filename.replace(i, 1, c, 1);
183 }
184 creationTime = FileTimeToTimet(info.ftCreationTime);
185 accessTime = FileTimeToTimet(info.ftLastAccessTime);
186 modTime = FileTimeToTimet(info.ftLastWriteTime);
187 attributes = info.dwFileAttributes;
188 filesize = info.nFileSizeLow;
189
190 struct stat origStat;
191 stat( filename.c_str(), &origStat);
192 modTime = origStat.st_mtime; // use stat(), since the LONGLONG code is unpredictable
193
194 //DEBUG_LOG(("FileInfo::set(): fname=%s, size=%d, modTime=%d\n", filename.c_str(), filesize, modTime));
195}
196
197//-------------------------------------------------------------------------------------------------
198
199Directory::Directory( const std::string& dirPath ) : m_dirPath(dirPath)
200{
201 WIN32_FIND_DATA item; // search item
202 HANDLE hFile; // handle for search resources
203 char currDir[ MAX_PATH ];
204
205 // sanity
206 if( !m_dirPath.length() )
207 {
208 return;
209 }
210
211 // save the current directory
212 GetCurrentDirectory( MAX_PATH, currDir );
213
214 // switch into the directory provided
215 if( SetCurrentDirectory( m_dirPath.c_str() ) == 0 )
216 {
217 return;
218 }
219
220 // go through each item in the output directory
221 bool done = false;
222 hFile = FindFirstFile( "*", &item);
223 if( hFile == INVALID_HANDLE_VALUE )
224 {
225 done = true;
226 }
227
228 FileInfo info;
229
230 while (!done)
231 {
232 // if this is a subdirectory keep the name around till the end
233 if( item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
234 {
235 if ( strcmp( item.cFileName, "." ) && strcmp( item.cFileName, ".." ) )
236 {
237 info.set(item);
238 m_subdirs.insert( info );
239 }
240 }
241 else
242 {
243 info.set(item);
244 m_files.insert( info );
245 }
246
247 if ( FindNextFile( hFile, &item ) == 0 )
248 {
249 done = true;
250 }
251 }
252
253 // close search
254 FindClose( hFile );
255
256 // restore the working directory to what it was when we started here
257 SetCurrentDirectory( currDir );
258}
259
261{
262 return &m_files;
263}
264
266{
267 return &m_subdirs;
268}
269
270//-------------------------------------------------------------------------------------------------
271
272// strtrim ====================================================================
274//=============================================================================
275static char* strtrim(char* buffer)
276{
277 if (buffer != NULL) {
278 // Strip leading white space from the string.
279 char * source = buffer;
280 while ((*source != 0) && ((unsigned char)*source <= 32))
281 {
282 source++;
283 }
284
285 if (source != buffer)
286 {
287 strcpy(buffer, source);
288 }
289
290 // Clip trailing white space from the string.
291 for (int index = strlen(buffer)-1; index >= 0; index--)
292 {
293 if ((*source != 0) && ((unsigned char)buffer[index] <= 32))
294 {
295 buffer[index] = '\0';
296 }
297 else
298 {
299 break;
300 }
301 }
302 }
303
304 return buffer;
305}
306
307//-------------------------------------------------------------------------------------------------
308typedef std::set<std::string> StringSet;
309
310//-------------------------------------------------------------------------------------------------
311void eraseCachedFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
312 StringSet& cachedFilesToErase)
313{
314 StringSet::const_iterator sit;
315 for (sit = cachedFilesToErase.begin(); sit != cachedFilesToErase.end(); ++sit)
316 {
317 std::string src = cacheDirName;
318 src.append("\\");
319 src.append(*sit);
320
321 DEBUG_LOG(("Erasing cached file: %s\n", src.c_str()));
322 DeleteFile(src.c_str());
323 }
324}
325
326//-------------------------------------------------------------------------------------------------
327void copyCachedFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
328 StringSet& cachedFilesToCopy)
329{
330 StringSet::const_iterator sit;
331 for (sit = cachedFilesToCopy.begin(); sit != cachedFilesToCopy.end(); ++sit)
332 {
333 std::string src = cacheDirName;
334 src.append("\\");
335 src.append(*sit);
336
337 std::string dest = targetDirName;
338 dest.append("\\");
339 dest.append(*sit);
340
341 DEBUG_LOG(("Copying cached file: %s\n", src.c_str()));
342 if (_chmod(dest.c_str(), _S_IWRITE | _S_IREAD) == -1)
343 {
344 DEBUG_LOG(("Cannot chmod '%s'\n", dest.c_str()));
345 }
346 CopyFile(src.c_str(), dest.c_str(), FALSE);
347 }
348}
349
350//-------------------------------------------------------------------------------------------------
351void compressOrigFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
352 StringSet& origFilesToCompress, const std::string& dxtOutFname)
353{
354 char tmpPath[_MAX_PATH] = "C:\\temp\\";
355 char tmpFname[_MAX_PATH] = "C:\\temp\\tmp.txt";
356 GetTempPath(_MAX_PATH, tmpPath);
357 GetTempFileName(tmpPath, "tex", 0, tmpFname);
358 HANDLE h = CreateFile(tmpFname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
359 if (!h)
360 {
361 DEBUG_LOG(("Could not create temp file '%s'! Unable to compress textures!\n", tmpFname));
362 }
363
364 StringSet::const_iterator sit;
365 for (sit = origFilesToCompress.begin(); sit != origFilesToCompress.end(); ++sit)
366 {
367 std::string tmp = sourceDirName;
368 tmp.append("\\");
369 tmp.append(*sit);
370 tmp.append("\n");
371 DEBUG_LOG(("Compressing file: %s", tmp.c_str()));
372 DWORD len;
373 WriteFile(h, tmp.c_str(), tmp.length(), &len, NULL);
374 }
375 CloseHandle(h);
376
377 std::string commandLine;
378 commandLine = "\\projects\\rts\\build\\nvdxt -list ";
379 commandLine.append(tmpFname);
380 commandLine.append(" -24 dxt1c -32 dxt5 -full -outdir ");
381 commandLine.append(cacheDirName);
382 commandLine.append(" > ");
383 commandLine.append(dxtOutFname);
384
385 DEBUG_LOG(("Compressing textures with command line of '%s'\n", commandLine.c_str()));
386 int ret = system(commandLine.c_str());
387 DEBUG_LOG(("system(%s) returned %d\n", commandLine.c_str(), ret));
388 DeleteFile(tmpFname);
389
390 // now copy compressed file to target dir
391 for (sit = origFilesToCompress.begin(); sit != origFilesToCompress.end(); ++sit)
392 {
393 std::string orig = sourceDirName;
394 orig.append("\\");
395 orig.append(*sit);
396
397 struct stat origStat;
398 stat( orig.c_str(), &origStat);
399
400 struct _utimbuf utb;
401 utb.actime = origStat.st_atime;
402 utb.modtime = origStat.st_mtime;
403
404 std::string src = cacheDirName;
405 src.append("\\");
406 src.append(*sit);
407 src.replace(src.size()-4, 4, ".dds");
408
409 _utime(src.c_str(), &utb);
410
411 std::string dest = targetDirName;
412 dest.append("\\");
413 dest.append(*sit);
414 dest.replace(dest.size()-4, 4, ".dds");
415
416 DEBUG_LOG(("Copying new file from %s to %s\n", src.c_str(), dest.c_str()));
417
418 if (_chmod(dest.c_str(), _S_IWRITE | _S_IREAD) == -1)
419 {
420 DEBUG_LOG(("Cannot chmod '%s'\n", dest.c_str()));
421 }
422 BOOL ret = CopyFile(src.c_str(), dest.c_str(), FALSE);
423 if (!ret)
424 {
425 DEBUG_LOG(("Could not copy file!\n"));
426 }
427
428 _utime(dest.c_str(), &utb);
429 }
430}
431
432//-------------------------------------------------------------------------------------------------
433void copyOrigFiles(const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName,
434 StringSet& origFilesToCopy)
435{
436 StringSet::const_iterator sit;
437 for (sit = origFilesToCopy.begin(); sit != origFilesToCopy.end(); ++sit)
438 {
439 std::string src = sourceDirName;
440 src.append("\\");
441 src.append(*sit);
442
443 std::string dest = targetDirName;
444 dest.append("\\");
445 dest.append(*sit);
446
447 if (_chmod(dest.c_str(), _S_IWRITE | _S_IREAD) == -1)
448 {
449 DEBUG_LOG(("Cannot chmod '%s'\n", dest.c_str()));
450 }
451 BOOL res = CopyFile(src.c_str(), dest.c_str(), FALSE);
452 DEBUG_LOG(("Copying file: %s returns %d\n", src.c_str(), res));
453 }
454}
455
456//-------------------------------------------------------------------------------------------------
457static void scanDir( const std::string& sourceDirName, const std::string& targetDirName, const std::string& cacheDirName, const std::string& dxtOutFname )
458{
459 DEBUG_LOG(("Scanning '%s'\n", sourceDirName.c_str()));
460 Directory sourceDir(sourceDirName);
461
462 DEBUG_LOG(("Scanning '%s'\n", targetDirName.c_str()));
463 Directory targetDir(targetDirName);
464
465 DEBUG_LOG(("Scanning '%s'\n", cacheDirName.c_str()));
466 Directory cacheDir(cacheDirName);
467
468 FileInfoSet *sourceFiles = sourceDir.getFiles();
469 FileInfoSet *cacheFiles = cacheDir.getFiles();
470 FileInfoSet *targetFiles = targetDir.getFiles();
471
472 StringSet cachedFilesToErase;
473 StringSet cachedFilesToCopy;
474 StringSet origFilesToCompress;
475 StringSet origFilesToCopy;
476
477 DEBUG_LOG(("Emptying targetDir\n"));
478 for (FileInfoSet::iterator targetIt = targetFiles->begin(); targetIt != targetFiles->end(); ++targetIt)
479 {
480 FileInfo f = *targetIt;
481 std::string fname = f.filename;
482 f.filename.replace(f.filename.size()-4, 4, ".tga");
483 FileInfoSet::iterator fit = sourceFiles->find(f);
484 if (fit == sourceFiles->end())
485 {
486 // look for pre-existing dds files too
487 f.filename.replace(f.filename.size()-4, 4, ".dds");
488 FileInfoSet::iterator ddsfit = sourceFiles->find(f);
489 if (ddsfit == sourceFiles->end())
490 {
491 fname.insert(0, "\\");
492 fname.insert(0, targetDirName);
493 DEBUG_LOG(("Deleting now-removed file '%s'\n", fname.c_str()));
494 DeleteFile(fname.c_str());
495 }
496 }
497 }
498
499 for (FileInfoSet::iterator cacheIt = cacheFiles->begin(); cacheIt != cacheFiles->end(); ++cacheIt)
500 {
501 FileInfo f = *cacheIt;
502 int len = f.filename.size();
503 if (len < 5)
504 {
505 cachedFilesToErase.insert(f.filename);
506 continue;
507 }
508 std::string fname = f.filename;
509 f.filename.replace(len-4, 4, ".tga");
510 FileInfoSet::iterator fit = sourceFiles->find(f);
511 if (fit != sourceFiles->end())
512 {
513 FileInfo sf = *fit;
514 if (f.modTime < sf.modTime)
515 {
537 cachedFilesToErase.insert(fname);
538 }
539 else
540 {
541 f.filename = fname; // back to .dds
542 FileInfoSet::iterator it = targetFiles->find(f);
543 if (it == targetFiles->end())
544 cachedFilesToCopy.insert(fname);
545 }
546 }
547 else
548 {
549 cachedFilesToErase.insert(fname);
550 }
551 }
552
553 for (FileInfoSet::iterator sourceIt = sourceFiles->begin(); sourceIt != sourceFiles->end(); ++sourceIt)
554 {
555 FileInfo f = *sourceIt;
556
557 std::string fname = f.filename;
558 const char *s = fname.c_str();
559 int index = 0;
560 const char *check = nodxtPrefix[0];
561 bool shouldSkip = false;
562 while (check)
563 {
564 if (fname.find(check) == 0)
565 {
566 shouldSkip = true;
567 break;
568 }
569 check = nodxtPrefix[++index];
570 }
571
572 index = 0;
573 check = nodxtAnywhere[0];
574 while (check && !shouldSkip)
575 {
576 if (fname.find(check) != fname.npos)
577 {
578 shouldSkip = true;
579 break;
580 }
581 check = nodxtAnywhere[++index];
582 }
583
584 if (!shouldSkip)
585 {
586 // check for preexisting .dds files so we can just copy them
587 if (fname.find(".dds") != fname.npos)
588 {
589 shouldSkip = true;
590 }
591 }
592
593 if (shouldSkip)
594 {
595 origFilesToCopy.insert(s);
596 }
597 else
598 {
599 int len = f.filename.size();
600 f.filename.replace(len-4, 4, ".dds");
601 FileInfoSet::iterator fit = cacheFiles->find(f);
602 if (fit != cacheFiles->end())
603 {
604 FileInfo cf = *fit;
605 if (cf.modTime < f.modTime)
606 {
607 origFilesToCompress.insert(fname);
608 }
609 }
610 else
611 {
612 origFilesToCompress.insert(fname);
613 }
614 }
615 }
616
617 // now dump our files
618 eraseCachedFiles (sourceDirName, targetDirName, cacheDirName, cachedFilesToErase);
619 copyCachedFiles (sourceDirName, targetDirName, cacheDirName, cachedFilesToCopy);
620 copyOrigFiles (sourceDirName, targetDirName, cacheDirName, origFilesToCopy);
621 compressOrigFiles(sourceDirName, targetDirName, cacheDirName, origFilesToCompress, dxtOutFname);
622}
623
624//-------------------------------------------------------------------------------------------------
625#define USE_WINMAIN
626#ifdef USE_WINMAIN
627int APIENTRY WinMain(HINSTANCE hInstance,
628 HINSTANCE hPrevInstance,
629 LPSTR lpCmdLine,
630 int nCmdShow)
631{
632 /*
633 ** Convert WinMain arguments to simple main argc and argv
634 */
635 int argc = 1;
636 char * argv[20];
637 argv[0] = NULL;
638
639 char * token = strtok(lpCmdLine, " ");
640 while (argc < 20 && token != NULL)
641 {
642 argv[argc++] = strtrim(token);
643 token = strtok(NULL, " ");
644 }
645#else
646int main(int argc, const char **argv)
647{
648#endif // USE_WINMAIN
649
650 if (argc != 6)
651 {
652 usage(argv[0]);
653 }
654 else
655 {
656 const char *sourceDir = argv[1];
657 const char *targetDir = argv[2];
658 const char *cacheDir = argv[3];
659
660#ifndef NDEBUG
661 theDebugMunkee = new DebugMunkee(argv[4]);
662#endif
663
664 //setUpLoadWindow();
665 scanDir(sourceDir, targetDir, cacheDir, argv[5]);
666 //setLoadWindowText("Writing to file...");
667 //printSet( noAlphaChannel, "No Alpha Channel" );
668 //printSet( noAlpha, "Not Using Alpha Channel" );
669 //printSet( hasAlpha, "Using Alpha Channel" );
670 //tearDownLoadWindow();
671
672#ifndef NDEBUG
673 delete theDebugMunkee;
674 theDebugMunkee = NULL;
675#endif
676 }
677
678 return 0;
679}
#define NULL
Definition BaseType.h:92
#define FALSE
Definition BaseType.h:113
unsigned long DWORD
Definition bittype.h:57
#define BOOL
Definition Wnd_File.h:57
DebugMunkee(const char *fname="debugLog.txt")
FileInfoSet m_files
Directory(const std::string &dirPath)
FileInfoSet * getSubdirs(void)
std::string m_dirPath
FileInfoSet * getFiles(void)
FileInfoSet m_subdirs
void main(void)
Definition test1.cpp:47
bool operator()(const FileInfo &a, const FileInfo &b) const
std::string filename
void set(const WIN32_FIND_DATA &info)
time_t creationTime
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
#define DEBUG_LOG(x)
void copyCachedFiles(const std::string &sourceDirName, const std::string &targetDirName, const std::string &cacheDirName, StringSet &cachedFilesToCopy)
void compressOrigFiles(const std::string &sourceDirName, const std::string &targetDirName, const std::string &cacheDirName, StringSet &origFilesToCompress, const std::string &dxtOutFname)
std::set< FileInfo, FileInfoComparator > FileInfoSet
std::set< std::string > StringSet
void eraseCachedFiles(const std::string &sourceDirName, const std::string &targetDirName, const std::string &cacheDirName, StringSet &cachedFilesToErase)
void copyOrigFiles(const std::string &sourceDirName, const std::string &targetDirName, const std::string &cacheDirName, StringSet &origFilesToCopy)
#define LOG(x)
char * strtrim(char *buffer)
Definition trim.cpp:60
long time_t
Definition wolapi.h:436