diff --git a/src/mission/KM_Campaigns.pas b/src/mission/KM_Campaigns.pas index 88f15eca15..aaa7f762db 100644 --- a/src/mission/KM_Campaigns.pas +++ b/src/mission/KM_Campaigns.pas @@ -171,8 +171,11 @@ implementation SysUtils, Math, KromUtils, KM_GameParams, KM_Resource, KM_ResLocales, KM_ResSprites, KM_ResTypes, - KM_Log, KM_Defaults, KM_CommonUtils, KM_FileIO; - + KM_Log, KM_Defaults, KM_CommonUtils, KM_FileIO, KM_Sort + {$IFDEF FPC} + , Generics.Defaults + {$ENDIF} + ; const CAMP_HEADER_V1 = $FEED; //Just some header to separate right progress files from wrong @@ -208,28 +211,45 @@ destructor TKMCampaignsCollection.Destroy; end; -procedure TKMCampaignsCollection.SortCampaigns; +// Return True if A is considered less (<) than B, False otherwise +function TKMCampaignComparator(constref A, B: TKMCampaign): Boolean; +var + aPrio, bPrio: Integer; +begin + // TSK goes first + if A.ShortName = 'TSK' then aPrio := 0 + // TPR goes second + else if A.ShortName = 'TPR' then aPrio := 1 + // Others go lexicographically sorted + else aPrio := 2; - //Return True if items should be exchanged - function Compare(A, B: TKMCampaign): Boolean; - begin - //TSK is first - if A.ShortName = 'TSK' then Result := False - else if B.ShortName = 'TSK' then Result := True - //TPR is second - else if A.ShortName = 'TPR' then Result := False - else if B.ShortName = 'TPR' then Result := True - //Others are left in existing order (alphabetical) - else Result := False; - end; + if B.ShortName = 'TSK' then bPrio := 0 + else if B.ShortName = 'TPR' then bPrio := 1 + else bPrio := 2; -var - I, K: Integer; + Result := (aPrio < bPrio) + or ((2 = aPrio) and (aPrio = bPrio) and (A.ShortName < B.ShortName)); +end; + + +{$IFDEF Unix} +// Return Negative if A < B, Positive if B < A, 0 otherwise +function TKMCampaignComparatorThreeWay(constref A, B: TKMCampaign): LongInt; begin - for I := 0 to Count - 1 do - for K := I to Count - 1 do - if Compare(Campaigns[I], Campaigns[K]) then - SwapInt(NativeUInt(fList.List[I]), NativeUInt(fList.List[K])); + if (TKMCampaignComparator(A, B)) then Result := -1 + else if (TKMCampaignComparator(B, A)) then Result := +1 + else Result := 0; +end; +{$ENDIF} + + +procedure TKMCampaignsCollection.SortCampaigns; +begin + {$IFNDEF Unix} + SelectionSort(fList, 0, Count - 1, @TKMCampaignComparator); + {$ELSE} + fList.Sort(@TKMCampaignComparatorThreeWay); + {$ENDIF} end; diff --git a/src/utils/algorithms/KM_Sort.pas b/src/utils/algorithms/KM_Sort.pas index f7daf2e9a3..991b9db223 100644 --- a/src/utils/algorithms/KM_Sort.pas +++ b/src/utils/algorithms/KM_Sort.pas @@ -2,8 +2,15 @@ {$I KaM_Remake.inc} interface +uses + KromUtils, + Generics.Collections, Generics.Defaults; + type TKMCompFunc = function (const aElem1, aElem2): Integer; + TKMSelectionSortCompType = Function(constref A, B: T) : Boolean; + +procedure SelectionSort(var aList: TList; idxFirst, idxLast: Integer; Comp: TKMSelectionSortCompType); procedure SortCustom(var aArr; aMinIdx, aMaxIdx, aSize: Integer; aCompFunc: TKMCompFunc); @@ -63,4 +70,37 @@ procedure SortCustom(var aArr; aMinIdx, aMaxIdx, aSize: Integer; aCompFunc: TKMC QuickSort(aMinIdx * aSize, aMaxIdx * aSize, Buf[0]); end; +procedure SelectionSort(var aList: TList; idxFirst, idxLast: Integer; Comp: TKMSelectionSortCompType); +var + I, K, L, J: Integer; +begin + if not (idxFirst < idxLast) then Exit; + + I := idxFirst; + L := idxLast; + + while I < L do + begin + J := I; + for K := J + 1 to L do + if + {$IFDEF Unix} + Comp(aList.Items[I], aList.Items[J]) + {$ELSE} + Comp(aList.List[I], aList.List[J]) + {$ENDIF} + then + J := K; + if (I <> J) then + begin + {$IFDEF Unix} + aList.Exchange(I, J); + {$ELSE} + SwapInt(NativeUInt(aList.List[I]), NativeUInt(aList.List[J])); + {$ENDIF} + end; + Inc(I); + end; +end; + end.