/*
** Command & Conquer Generals Zero Hour(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 .
*/
/////////////////////////////////////////////////////////////////////////EA-V1
// $File: //depot/GeneralsMD/Staging/code/Tools/assetcull/assetcull.cpp $
// $Author: mhoffe $
// $Revision: #1 $
// $DateTime: 2003/07/28 14:54:04 $
//
// ©2003 Electronic Arts
//
// simple recursive directory compare tool for asset culling
//////////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#include
/*
Usage: assetcull
Description:
All files in and (and subdirectories) are compared
binary. If an identical file exists it is removed from
and a corresponding DEL line is written to the given batch file.
*/
static bool filesEqual(const char *fn1, const char *fn2)
{
// make sure both files exist and are of same size
struct _stat s1,s2;
if (_stat(fn1,&s1)||
_stat(fn2,&s2))
return false;
if (s1.st_size!=s2.st_size)
return false;
// must compare byte-by-byte
FILE *f1=fopen(fn1,"rb");
if (!f1)
return false;
FILE *f2=fopen(fn2,"rb");
if (!f2)
return false;
static char buf1[16384],buf2[16384];
for (unsigned k=0;ksizeof(buf1))
cur=sizeof(buf1);
if (fread(buf1,cur,1,f1)!=1||
fread(buf2,cur,1,f2)!=1)
break;
if (memcmp(buf1,buf2,cur))
break;
k+=cur;
}
fclose(f1);
fclose(f2);
return k==s1.st_size;
}
static int recursiveCull(FILE *batchFile,
const char *dir1, const char *dir2,
const char *relDir)
{
// sub directory must exist both in dir1 and dir2
// (but we're walking dir2 only later)
std::string work;
work=dir1; work+=relDir; work+="*.*";
_finddata_t fd;
long h=_findfirst(work.c_str(),&fd);
if (h==-1)
return 0;
_findclose(h);
work=dir2; work+=relDir; work+="*.*";
h=_findfirst(work.c_str(),&fd);
if (h==-1)
return 0;
// walk dir2, collect sub directories and check for duplicate
// files
std::vector subdir,dupfiles;
int deleted=0;
for (;;)
{
if (fd.attrib&_A_SUBDIR)
{
if (strcmp(fd.name,".")&&
strcmp(fd.name,".."))
subdir.push_back(fd.name);
}
else
{
std::string work1,work2;
work1=dir1; work1+=relDir; work1+=fd.name;
work2=dir2; work2+=relDir; work2+=fd.name;
if (filesEqual(work1.c_str(),work2.c_str()))
dupfiles.push_back(fd.name);
}
if (_findnext(h,&fd))
break;
}
_findclose(h);
// remove duplicate files, at to batch file
// (we can't just delete them inside the find loop because - at
// least theoretically - that could screw up that find process...)
for (std::vector::iterator i=dupfiles.begin();i!=dupfiles.end();++i)
{
std::string work;
work=dir1; work+=relDir; work+=*i;
_chmod(work.c_str(),_S_IREAD|_S_IWRITE);
if (_unlink(work.c_str()))
fprintf(stderr,"Error: Can't delete %s\n",work.c_str());
else
deleted++;
fprintf(batchFile,"attrib -r \"%s\"\n",work.c_str());
fprintf(batchFile,"del -r \"%s\"\n",work.c_str());
}
// walk subdirectories
for (i=subdir.begin();i!=subdir.end();++i)
{
std::string work;
work=relDir;
work+=*i;
work+="\\";
deleted+=recursiveCull(batchFile,dir1,dir2,work.c_str());
}
// done!
return deleted;
}
int main(int argc, char *argv[])
{
if (argc!=4)
{
printf("Usage: assetcull \n\n"
"Description:\n"
" All files in and (and subdirectories) are compared\n"
" binary. If an identical file exists it is removed from \n"
" and a corresponding DEL line is written to the given batch file.\n"
);
return 10;
}
FILE *f=fopen(argv[3],"wt");
if (!f)
{
printf("Error: Can't create %s\n",argv[3]);
return 10;
}
int n=recursiveCull(f,argv[1],argv[2],"\\.\\");
fclose(f);
printf("assetcull: %i files culled.\n",n);
return 0;
}