@@ -33,6 +33,16 @@ public Repository Repository
3333 get => _repo ;
3434 }
3535
36+ public IReadOnlyDictionary < string , string > DecodedPaths
37+ {
38+ get
39+ {
40+ if ( _repo == null || ! _repo . Settings . EnableUnrealEngineSupport || ! _repo . Settings . EnableOFPADecoding )
41+ return null ;
42+ return _decodedPaths ;
43+ }
44+ }
45+
3646 public int ActiveTabIndex
3747 {
3848 get => _sharedData . ActiveTabIndex ;
@@ -173,10 +183,16 @@ public CommitDetail(Repository repo, CommitDetailSharedData sharedData)
173183 _repo = repo ;
174184 _sharedData = sharedData ?? new CommitDetailSharedData ( ) ;
175185 WebLinks = Models . CommitLink . Get ( repo . Remotes ) ;
186+
187+ if ( _repo != null )
188+ _repo . PropertyChanged += OnRepositoryPropertyChanged ;
176189 }
177190
178191 public void Dispose ( )
179192 {
193+ if ( _repo != null )
194+ _repo . PropertyChanged -= OnRepositoryPropertyChanged ;
195+
180196 _repo = null ;
181197 _commit = null ;
182198 _changes = null ;
@@ -190,6 +206,7 @@ public void Dispose()
190206 _requestingRevisionFiles = false ;
191207 _revisionFiles = null ;
192208 _revisionFileSearchSuggestion = null ;
209+ _decodedPaths = null ;
193210 }
194211
195212 public void NavigateTo ( string commitSHA )
@@ -444,6 +461,9 @@ private void Refresh()
444461 SelectedChanges = null ;
445462 else
446463 SelectedChanges = [ VisibleChanges [ 0 ] ] ;
464+
465+ if ( _repo . Settings . EnableUnrealEngineSupport && _repo . Settings . EnableOFPADecoding )
466+ _currentDecodeTask = DecodeOFPAPathsAsync ( changes ) ;
447467 } ) ;
448468 }
449469 } , token ) ;
@@ -647,6 +667,80 @@ private async Task SetViewingCommitAsync(Models.Object file)
647667 [ GeneratedRegex ( @"`.*?`" ) ]
648668 private static partial Regex REG_INLINECODE_FORMAT ( ) ;
649669
670+ private void OnRepositoryPropertyChanged ( object sender , System . ComponentModel . PropertyChangedEventArgs e )
671+ {
672+ if ( e . PropertyName == nameof ( Repository . EnableUnrealEngineSupport ) )
673+ {
674+ OnPropertyChanged ( nameof ( DecodedPaths ) ) ;
675+ if ( _repo . Settings . EnableUnrealEngineSupport && _repo . Settings . EnableOFPADecoding )
676+ _currentDecodeTask = DecodeOFPAPathsAsync ( _changes ) ;
677+ }
678+ }
679+
680+ private async Task DecodeOFPAPathsAsync ( List < Models . Change > changes )
681+ {
682+ await _currentDecodeTask . ConfigureAwait ( false ) ;
683+
684+ if ( _repo == null || _commit == null ||
685+ ! _repo . Settings . EnableUnrealEngineSupport ||
686+ ! _repo . Settings . EnableOFPADecoding ||
687+ changes == null || changes . Count == 0 )
688+ return ;
689+
690+ var repositoryPath = _repo . FullPath ;
691+ var parent = _commit . Parents . Count == 0 ? Models . Commit . EmptyTreeSHA1 : $ "{ _commit . SHA } ^";
692+ var results = new Dictionary < string , string > ( StringComparer . Ordinal ) ;
693+
694+ await Task . Run ( async ( ) =>
695+ {
696+ var objectSpecs = new List < ( string RelativePath , string Spec ) > ( ) ;
697+ foreach ( var change in changes )
698+ {
699+ if ( Utilities . OFPAParser . IsOFPAFile ( change . Path ) )
700+ {
701+ // For deleted files, read from parent. For others, read from current commit.
702+ var spec = ( change . WorkTree == Models . ChangeState . Deleted || change . Index == Models . ChangeState . Deleted )
703+ ? $ "{ parent } :{ change . Path } "
704+ : $ "{ _commit . SHA } :{ change . Path } ";
705+ objectSpecs . Add ( ( change . Path , spec ) ) ;
706+ }
707+ }
708+
709+ if ( objectSpecs . Count == 0 )
710+ return ;
711+
712+ var specs = new List < string > ( ) ;
713+ foreach ( var entry in objectSpecs )
714+ specs . Add ( entry . Spec ) ;
715+
716+ var dataMap = await Commands . QueryFileContent . RunBatchAsync ( repositoryPath , specs , MaxOFPASampleSize ) . ConfigureAwait ( false ) ;
717+ foreach ( var entry in objectSpecs )
718+ {
719+ if ( dataMap . TryGetValue ( entry . Spec , out var data ) )
720+ {
721+ var decoded = Utilities . OFPAParser . DecodeFromData ( data ) ;
722+ results [ entry . RelativePath ] = decoded ? . LabelValue ;
723+ }
724+ }
725+
726+ var updated = new Dictionary < string , string > ( StringComparer . Ordinal ) ;
727+ foreach ( var kvp in results )
728+ updated [ kvp . Key ] = kvp . Value ;
729+ _decodedPaths = updated ;
730+ } ) ;
731+
732+ if ( results . Count > 0 )
733+ {
734+ await Dispatcher . UIThread . InvokeAsync ( ( ) =>
735+ {
736+ if ( _repo == null || ! _repo . Settings . EnableUnrealEngineSupport || ! _repo . Settings . EnableOFPADecoding )
737+ return ;
738+
739+ OnPropertyChanged ( nameof ( DecodedPaths ) ) ;
740+ } ) ;
741+ }
742+ }
743+
650744 private Repository _repo = null ;
651745 private CommitDetailSharedData _sharedData = null ;
652746 private Models . Commit _commit = null ;
@@ -667,5 +761,8 @@ private async Task SetViewingCommitAsync(Models.Object file)
667761 private List < string > _revisionFileSearchSuggestion = null ;
668762 private bool _canOpenRevisionFileWithDefaultEditor = false ;
669763 private Vector _scrollOffset = Vector . Zero ;
764+ private Dictionary < string , string > _decodedPaths = null ;
765+ private Task _currentDecodeTask = Task . CompletedTask ;
766+ private const int MaxOFPASampleSize = 256 * 1024 ;
670767 }
671768}
0 commit comments