11using System ;
22using System . Collections . Generic ;
3+ using System . Diagnostics ;
34using System . Threading ;
45using AET . ModVerify . Reporting ;
56using AET . ModVerify . Settings ;
@@ -182,14 +183,31 @@ private void VerifyModelClass(ModelClass modelClass, IReadOnlyCollection<string>
182183 }
183184 catch ( BinaryCorruptedException e )
184185 {
185- var message = $ "'{ fileName } ' is corrupted: { e . Message } ";
186- AddError ( VerificationError . Create (
187- this ,
188- VerifierErrorCodes . BinaryFileCorrupt ,
189- message ,
190- VerificationSeverity . Critical ,
191- contextInfo ,
192- NormalizeFileName ( fileName ) ) ) ;
186+ if ( ! CheckBinaryCorruptedFileIsActuallyRenderable ( fileName , out var actualFilePath ) )
187+ {
188+ var message = $ "Possible file CRC32 collision: '{ fileName } ' was requested but '{ actualFilePath } ' was found by the engine.";
189+ AddError ( VerificationError . Create (
190+ this ,
191+ VerifierErrorCodes . UnexpectedFileLoad ,
192+ message ,
193+ // Error, because loading a model/particle directly impacts game behavior and would be very hard to debug
194+ // for mod creators, unaware of the CRC32 collision issue.
195+ VerificationSeverity . Error ,
196+ contextInfo ,
197+ NormalizeFileName ( fileName ) ) ) ;
198+ }
199+ else
200+ {
201+ var message = $ "'{ fileName } ' is corrupted: { e . Message } ";
202+ AddError ( VerificationError . Create (
203+ this ,
204+ VerifierErrorCodes . BinaryFileCorrupt ,
205+ message ,
206+ VerificationSeverity . Critical ,
207+ contextInfo ,
208+ NormalizeFileName ( fileName ) ) ) ;
209+ }
210+
193211 exists = true ;
194212 return null ;
195213 }
@@ -213,20 +231,39 @@ private void VerifyModelClass(ModelClass modelClass, IReadOnlyCollection<string>
213231 alamoFile . FileName , @"DATA\ART\MODELS" , true ,
214232 ( _ , _ , alaFile ) =>
215233 {
216- var alaFileName = NormalizeFileName ( alaFile ) ;
217- AddError ( VerificationError . Create (
218- this ,
219- VerifierErrorCodes . BinaryFileCorrupt ,
220- $ "Invalid animation file '{ alaFileName } ' for model '{ alamoFile . FileName } '",
221- VerificationSeverity . Error ,
222- [ NormalizeFileName ( alamoFile . FileName ) ] ,
223- alaFileName ) ) ;
234+ var alaFileName = NormalizeFileName ( alaFile ) ;
235+
236+ if ( ! CheckBinaryCorruptedFileIsActuallyRenderable ( alaFileName , out var actualFilePath ) )
237+ {
238+ var message =
239+ $ "Possible file CRC32 collision: '{ fileName } ' was requested but '{ actualFilePath } ' was found by the engine.";
240+ AddError ( VerificationError . Create (
241+ this ,
242+ VerifierErrorCodes . UnexpectedFileLoad ,
243+ message ,
244+ // Information, because for animations, as there is more likely to be a CRC32 collision than an actual corrupted file.
245+ // This is because the engine attempts to load all possible animations for each model and thus
246+ // there are simply more chances for a CRC32 collision.
247+ VerificationSeverity . Information ,
248+ contextInfo ,
249+ NormalizeFileName ( fileName ) ) ) ;
250+ }
251+ else
252+ {
253+ AddError ( VerificationError . Create (
254+ this ,
255+ VerifierErrorCodes . BinaryFileCorrupt ,
256+ $ "Invalid animation file '{ alaFileName } ' for model '{ alamoFile . FileName } '",
257+ VerificationSeverity . Error ,
258+ [ NormalizeFileName ( alamoFile . FileName ) ] ,
259+ alaFileName ) ) ;
260+ }
224261 } ) ;
225262 }
226263
227264 return new ModelClass ( alamoFile , animationCollection ) ;
228265 }
229-
266+
230267 private void VerifyParticle ( IAloParticleFile file , IReadOnlyCollection < string > contextInfo )
231268 {
232269 IReadOnlyList < string > particleContext = [ .. contextInfo , NormalizeFileName ( file . FileName ) ] ;
@@ -396,6 +433,21 @@ private void VerifyShaderExists(IPetroglyphFileHolder model, string shader, IRea
396433 }
397434 }
398435
436+ // NB: This method assures that the BinaryCorruptedException resulted from a file
437+ // that is actually an Alamo file (and thus should be reported as a corrupted file),
438+ // and not from some other file that was found due to e.g., CRC32 collision.
439+ private bool CheckBinaryCorruptedFileIsActuallyRenderable ( string fileName , out string actualFilePath )
440+ {
441+ var filePath = FileSystem . Path . Join ( @"DATA\ART\MODELS" , fileName ) ;
442+ var exists = GameEngine . GameRepository . FileExists ( filePath , false , out _ , out actualFilePath ! ) ;
443+ Debug . Assert ( exists ) ;
444+
445+ var extension = FileSystem . Path . GetExtension ( actualFilePath ) ;
446+
447+ return string . IsNullOrEmpty ( actualFilePath ) || extension . Equals ( ".alo" , StringComparison . OrdinalIgnoreCase ) ||
448+ extension . Equals ( ".ala" , StringComparison . OrdinalIgnoreCase ) ;
449+ }
450+
399451 private string NormalizeFileName ( string fileName )
400452 {
401453 return GameEngine . GameRepository . PGFileSystem . GetFileName ( fileName ) . ToUpperInvariant ( ) ;
0 commit comments