assetcull.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. /////////////////////////////////////////////////////////////////////////EA-V1
  19. // $File: //depot/GeneralsMD/Staging/code/Tools/assetcull/assetcull.cpp $
  20. // $Author: mhoffe $
  21. // $Revision: #1 $
  22. // $DateTime: 2003/07/28 14:54:04 $
  23. //
  24. // ©2003 Electronic Arts
  25. //
  26. // simple recursive directory compare tool for asset culling
  27. //////////////////////////////////////////////////////////////////////////////
  28. #include <string>
  29. #include <vector>
  30. #include <sys/stat.h>
  31. #include <stdio.h>
  32. #include <io.h>
  33. /*
  34. Usage: assetcull <dir1> <dir2> <bat-file>
  35. Description:
  36. All files in <dir1> and <dir2> (and subdirectories) are compared
  37. binary. If an identical file exists it is removed from <dir1>
  38. and a corresponding DEL line is written to the given batch file.
  39. */
  40. static bool filesEqual(const char *fn1, const char *fn2)
  41. {
  42. // make sure both files exist and are of same size
  43. struct _stat s1,s2;
  44. if (_stat(fn1,&s1)||
  45. _stat(fn2,&s2))
  46. return false;
  47. if (s1.st_size!=s2.st_size)
  48. return false;
  49. // must compare byte-by-byte
  50. FILE *f1=fopen(fn1,"rb");
  51. if (!f1)
  52. return false;
  53. FILE *f2=fopen(fn2,"rb");
  54. if (!f2)
  55. return false;
  56. static char buf1[16384],buf2[16384];
  57. for (unsigned k=0;k<s1.st_size;)
  58. {
  59. unsigned cur=s1.st_size-k;
  60. if (cur>sizeof(buf1))
  61. cur=sizeof(buf1);
  62. if (fread(buf1,cur,1,f1)!=1||
  63. fread(buf2,cur,1,f2)!=1)
  64. break;
  65. if (memcmp(buf1,buf2,cur))
  66. break;
  67. k+=cur;
  68. }
  69. fclose(f1);
  70. fclose(f2);
  71. return k==s1.st_size;
  72. }
  73. static int recursiveCull(FILE *batchFile,
  74. const char *dir1, const char *dir2,
  75. const char *relDir)
  76. {
  77. // sub directory must exist both in dir1 and dir2
  78. // (but we're walking dir2 only later)
  79. std::string work;
  80. work=dir1; work+=relDir; work+="*.*";
  81. _finddata_t fd;
  82. long h=_findfirst(work.c_str(),&fd);
  83. if (h==-1)
  84. return 0;
  85. _findclose(h);
  86. work=dir2; work+=relDir; work+="*.*";
  87. h=_findfirst(work.c_str(),&fd);
  88. if (h==-1)
  89. return 0;
  90. // walk dir2, collect sub directories and check for duplicate
  91. // files
  92. std::vector<std::string> subdir,dupfiles;
  93. int deleted=0;
  94. for (;;)
  95. {
  96. if (fd.attrib&_A_SUBDIR)
  97. {
  98. if (strcmp(fd.name,".")&&
  99. strcmp(fd.name,".."))
  100. subdir.push_back(fd.name);
  101. }
  102. else
  103. {
  104. std::string work1,work2;
  105. work1=dir1; work1+=relDir; work1+=fd.name;
  106. work2=dir2; work2+=relDir; work2+=fd.name;
  107. if (filesEqual(work1.c_str(),work2.c_str()))
  108. dupfiles.push_back(fd.name);
  109. }
  110. if (_findnext(h,&fd))
  111. break;
  112. }
  113. _findclose(h);
  114. // remove duplicate files, at to batch file
  115. // (we can't just delete them inside the find loop because - at
  116. // least theoretically - that could screw up that find process...)
  117. for (std::vector<std::string>::iterator i=dupfiles.begin();i!=dupfiles.end();++i)
  118. {
  119. std::string work;
  120. work=dir1; work+=relDir; work+=*i;
  121. _chmod(work.c_str(),_S_IREAD|_S_IWRITE);
  122. if (_unlink(work.c_str()))
  123. fprintf(stderr,"Error: Can't delete %s\n",work.c_str());
  124. else
  125. deleted++;
  126. fprintf(batchFile,"attrib -r \"%s\"\n",work.c_str());
  127. fprintf(batchFile,"del -r \"%s\"\n",work.c_str());
  128. }
  129. // walk subdirectories
  130. for (i=subdir.begin();i!=subdir.end();++i)
  131. {
  132. std::string work;
  133. work=relDir;
  134. work+=*i;
  135. work+="\\";
  136. deleted+=recursiveCull(batchFile,dir1,dir2,work.c_str());
  137. }
  138. // done!
  139. return deleted;
  140. }
  141. int main(int argc, char *argv[])
  142. {
  143. if (argc!=4)
  144. {
  145. printf("Usage: assetcull <dir1> <dir2> <bat-file>\n\n"
  146. "Description:\n"
  147. " All files in <dir1> and <dir2> (and subdirectories) are compared\n"
  148. " binary. If an identical file exists it is removed from <dir1>\n"
  149. " and a corresponding DEL line is written to the given batch file.\n"
  150. );
  151. return 10;
  152. }
  153. FILE *f=fopen(argv[3],"wt");
  154. if (!f)
  155. {
  156. printf("Error: Can't create %s\n",argv[3]);
  157. return 10;
  158. }
  159. int n=recursiveCull(f,argv[1],argv[2],"\\.\\");
  160. fclose(f);
  161. printf("assetcull: %i files culled.\n",n);
  162. return 0;
  163. }