Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 23 additions & 19 deletions src/SwfocTrainer.Profiles/Services/ModOnboardingService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma warning disable S4136
#pragma warning disable S4136
using System.Globalization;
using System.Text.RegularExpressions;
using SwfocTrainer.Core.Contracts;
Expand Down Expand Up @@ -307,7 +307,8 @@
p.Warnings.Add("No local path hints were inferred from launch samples.");
}

var metadata = BuildSeedMetadata(p.Seed, p.SourceRunId, baseProfile.Id, workshopIds, aliases, pathHints, requiredCapabilities);
var metadataInput = new SeedMetadataInput(p.Seed, p.SourceRunId, baseProfile.Id, workshopIds, aliases, pathHints, requiredCapabilities);
var metadata = BuildSeedMetadata(metadataInput);

var draftProfile = new TrainerProfile(
Id: p.ProfileId,
Expand Down Expand Up @@ -342,26 +343,29 @@
return new SeedScaffoldResult(workshopIds, pathHints, aliases, outputPath);
}

private static Dictionary<string, string> BuildSeedMetadata(
GeneratedProfileSeed seed,
string sourceRunId,
string baseProfileId,
IReadOnlyList<string> workshopIds,
IReadOnlyList<string> aliases,
IReadOnlyList<string> pathHints,
IReadOnlyList<string> requiredCapabilities)
private sealed record SeedMetadataInput(
GeneratedProfileSeed Seed,
string SourceRunId,
string BaseProfileId,
IReadOnlyList<string> WorkshopIds,
IReadOnlyList<string> Aliases,
IReadOnlyList<string> PathHints,
IReadOnlyList<string> RequiredCapabilities);

private static Dictionary<string, string> BuildSeedMetadata(SeedMetadataInput input)
{
var seed = input.Seed;
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["origin"] = "auto_discovery",
["sourceRunId"] = sourceRunId.Trim(),
["sourceRunId"] = input.SourceRunId.Trim(),
["confidence"] = seed.Confidence.ToString("0.00", CultureInfo.InvariantCulture),
["parentProfile"] = ResolveParentProfile(seed, baseProfileId)
["parentProfile"] = ResolveParentProfile(seed, input.BaseProfileId)
};

AddMetadataIfNotEmpty(metadata, "requiredWorkshopIds", workshopIds);
AddMetadataIfNotEmpty(metadata, "profileAliases", aliases);
AddMetadataIfNotEmpty(metadata, "localPathHints", pathHints);
AddMetadataIfNotEmpty(metadata, "requiredWorkshopIds", input.WorkshopIds);
AddMetadataIfNotEmpty(metadata, "profileAliases", input.Aliases);
AddMetadataIfNotEmpty(metadata, "localPathHints", input.PathHints);
AddTrimmedMetadata(metadata, "onboardingNotes", seed.Notes);
AddTrimmedMetadata(metadata, "seedTitle", seed.Title);
AddTrimmedMetadata(metadata, "workshopId", seed.WorkshopId);
Expand All @@ -375,7 +379,7 @@
AddFilteredListMetadata(metadata, "parentDependencies", seed.ParentDependencies);
AddFilteredListMetadata(metadata, "launchHints", seed.LaunchHints);
AddFilteredListMetadata(metadata, "anchorHints", seed.AnchorHints);
AddMetadataIfNotEmpty(metadata, "requiredCapabilities", requiredCapabilities);
AddMetadataIfNotEmpty(metadata, "requiredCapabilities", input.RequiredCapabilities);

return metadata;
}
Expand Down Expand Up @@ -583,7 +587,7 @@
}
}

foreach (var hint in inferred.Where(x => !string.IsNullOrWhiteSpace(x) && IsPathHintCandidate(x))) // NOSONAR
foreach (var hint in inferred.Where(x => !string.IsNullOrWhiteSpace(x) && IsPathHintCandidate(x)))
{
merged.Add(hint);
}
Expand Down Expand Up @@ -637,7 +641,7 @@
return ids.OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToArray();
}

private static IReadOnlyList<string> InferPathHints(IReadOnlyList<ModLaunchSample> samples) // NOSONAR
private static IReadOnlyList<string> InferPathHints(IReadOnlyList<ModLaunchSample> samples)

Check failure on line 644 in src/SwfocTrainer.Profiles/Services/ModOnboardingService.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=Prekzursil_SWFOC-Mod-Menu&issues=AZ1ajKxtiq29Pk22toam&open=AZ1ajKxtiq29Pk22toam&pullRequest=137
{
var hints = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

Expand Down Expand Up @@ -713,7 +717,7 @@
.Where(normalized => !string.IsNullOrWhiteSpace(normalized));
}

private static IReadOnlyList<string> InferAliases(string profileId, string displayName, IReadOnlyList<string>? userAliases) // NOSONAR
private static IReadOnlyList<string> InferAliases(string profileId, string displayName, IReadOnlyList<string>? userAliases)

Check failure on line 720 in src/SwfocTrainer.Profiles/Services/ModOnboardingService.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=Prekzursil_SWFOC-Mod-Menu&issues=AZ1ajKxtiq29Pk22toal&open=AZ1ajKxtiq29Pk22toal&pullRequest=137
{
var aliases = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
Expand Down
82 changes: 34 additions & 48 deletions src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
{
private readonly record struct FloatScanCriteria(float Value, float Tolerance);

private readonly record struct RegionScanContext(
nint Handle,
nint RegionBase,
long RegionSize,
List<nint> Results,
int MaxResults,
CancellationToken CancellationToken);

private readonly record struct ChunkReadContext(
nint Handle,
nint RegionBase,
long Offset,
int ToRead);

internal readonly record struct FloatApproxScanRequest(
int ProcessId,
float Value,
Expand Down Expand Up @@ -39,7 +53,7 @@
maxResults,
cancellationToken,
(handle, regionBase, regionSize, results, max, token) =>
ScanRegion(handle, regionBase, regionSize, value, results, max, token));
ScanRegion(new RegionScanContext(handle, regionBase, regionSize, results, max, token), value));
}

public static IReadOnlyList<nint> ScanFloatApprox(
Expand Down Expand Up @@ -67,56 +81,36 @@
maxResults,
cancellationToken,
(handle, regionBase, regionSize, results, max, token) =>
ScanRegionFloatApprox(handle, regionBase, regionSize, criteria, results, max, token));
ScanRegionFloatApprox(new RegionScanContext(handle, regionBase, regionSize, results, max, token), criteria));
}

private static void ScanRegion(
nint handle,
nint regionBase,
long regionSize,
int value,
List<nint> results,
int maxResults,
CancellationToken cancellationToken)
RegionScanContext ctx,
int value)
{
ScanRegionChunks(
handle,
regionBase,
regionSize,
results,
maxResults,
cancellationToken,
ctx,
(buffer, read, chunkBase) =>
{
for (var i = 0; i <= read - 4 && results.Count < maxResults; i++)
for (var i = 0; i <= read - 4 && ctx.Results.Count < ctx.MaxResults; i++)
{
if (BitConverter.ToInt32(buffer, i) == value)
{
results.Add(chunkBase + i);
ctx.Results.Add(chunkBase + i);

Check failure on line 99 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L99

Potential null dereference detected.
}
}
});
}

private static void ScanRegionFloatApprox(
nint handle,
nint regionBase,
long regionSize,
FloatScanCriteria criteria,
List<nint> results,
int maxResults,
CancellationToken cancellationToken)
RegionScanContext ctx,
FloatScanCriteria criteria)
{
ScanRegionChunks(
handle,
regionBase,
regionSize,
results,
maxResults,
cancellationToken,
ctx,
(buffer, read, chunkBase) =>
{
for (var i = 0; i <= read - 4 && results.Count < maxResults; i += 4)
for (var i = 0; i <= read - 4 && ctx.Results.Count < ctx.MaxResults; i += 4)

Check failure on line 113 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L113

Potential null dereference detected.
{
var candidate = BitConverter.ToSingle(buffer, i);
if (!float.IsFinite(candidate))
Expand All @@ -126,40 +120,35 @@

if (MathF.Abs(candidate - criteria.Value) <= criteria.Tolerance)
{
results.Add(chunkBase + i);
ctx.Results.Add(chunkBase + i);
}
}
});
}

private static void ScanRegionChunks(
nint handle,
nint regionBase,
long regionSize,
List<nint> results,
int maxResults,
CancellationToken cancellationToken,
RegionScanContext ctx,
Action<byte[], int, nint> chunkScanner)
{
const int chunkSize = 64 * 1024;
var buffer = new byte[chunkSize];

for (long offset = 0; offset < regionSize && results.Count < maxResults; offset += chunkSize)
for (long offset = 0; offset < ctx.RegionSize && ctx.Results.Count < ctx.MaxResults; offset += chunkSize)

Check failure on line 136 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L136

Potential null dereference detected.
{
cancellationToken.ThrowIfCancellationRequested();
var toRead = (int)Math.Min(chunkSize, regionSize - offset);
ctx.CancellationToken.ThrowIfCancellationRequested();

Check failure on line 138 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L138

Potential null dereference detected.
var toRead = (int)Math.Min(chunkSize, ctx.RegionSize - offset);

Check failure on line 139 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L139

Potential null dereference detected.
if (toRead <= 0)
{
break;
}

buffer = EnsureBufferSize(buffer, toRead);
if (!TryReadChunk(handle, regionBase, offset, buffer, toRead, out var read))
if (!TryReadChunk(new ChunkReadContext(ctx.Handle, ctx.RegionBase, offset, toRead), buffer, out var read))

Check failure on line 146 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L146

Potential null dereference detected.
{
continue;
}

chunkScanner(buffer, read, regionBase + (nint)offset);
chunkScanner(buffer, read, ctx.RegionBase + (nint)offset);

Check failure on line 151 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L151

Potential null dereference detected.
}
}

Expand All @@ -169,15 +158,12 @@
}

private static bool TryReadChunk(
nint handle,
nint regionBase,
long offset,
ChunkReadContext ctx,
byte[] buffer,
int toRead,
out int read)
{
read = 0;
if (!NativeMethods.ReadProcessMemory(handle, regionBase + (nint)offset, buffer, toRead, out var readRaw))
if (!NativeMethods.ReadProcessMemory(ctx.Handle, ctx.RegionBase + (nint)ctx.Offset, buffer, ctx.ToRead, out var readRaw))

Check notice

Code scanning / CodeQL

Calls to unmanaged code Note

Replace this call with a call to managed code if possible.

Check failure on line 166 in src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Scanning/ProcessMemoryScanner.cs#L166

Potential null dereference detected.
{
return false;
}
Expand Down
69 changes: 30 additions & 39 deletions src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Text.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using SwfocTrainer.Core.Contracts;
using SwfocTrainer.Core.Models;
Expand Down Expand Up @@ -233,28 +233,19 @@
.ToArray();
if (missingRequired.Length > 0)
{
return BuildRequiredAnchorsMissingResult(
input.RequestedProfileId,
input.OperationId,
input.Operation,
input.Fingerprint,
matched,
missingRequired,
input.CapabilityHint);
var requiredCtx = new AnchorValidationContext(
input.RequestedProfileId, input.OperationId, matched, missingRequired, input.CapabilityHint);

Check failure on line 237 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L237

Potential null dereference detected.
return BuildRequiredAnchorsMissingResult(requiredCtx, input.Operation, input.Fingerprint);

Check failure on line 238 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L238

Potential null dereference detected.
}

var missingOptional = input.Operation.OptionalAnchors
.Where(anchor => !ContainsAnchor(input.ResolvedAnchors, anchor, comparer))
.ToArray();
if (missingOptional.Length > 0)
{
return BuildOptionalAnchorsMissingResult(
input.RequestedProfileId,
input.OperationId,
input.Fingerprint.FingerprintId,
matched,
missingOptional,
input.CapabilityHint);
var optionalCtx = new AnchorValidationContext(
input.RequestedProfileId, input.OperationId, matched, missingOptional, input.CapabilityHint);

Check failure on line 247 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L247

Potential null dereference detected.
return BuildOptionalAnchorsMissingResult(optionalCtx, input.Fingerprint.FingerprintId);

Check failure on line 248 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L248

Potential null dereference detected.
}

return BuildAllRequiredAnchorsPresentResult(
Expand All @@ -266,44 +257,36 @@
}

private static CapabilityResolutionResult BuildRequiredAnchorsMissingResult(
string requestedProfileId,
string operationId,
AnchorValidationContext ctx,
CapabilityOperationMap operation,
BinaryFingerprint fingerprint,
IReadOnlyList<string> matchedAnchors,
IReadOnlyList<string> missingAnchors,
CapabilityAvailabilityHint? capabilityHint)
BinaryFingerprint fingerprint)
{
return new CapabilityResolutionResult(
requestedProfileId,
operationId,
ctx.RequestedProfileId,

Check failure on line 265 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L265

Potential null dereference detected.
ctx.OperationId,

Check failure on line 266 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L266

Potential null dereference detected.
SdkCapabilityStatus.Degraded,
CapabilityReasonCode.RequiredAnchorsMissing,
BuildConfidence(matchedAnchors.Count, operation.RequiredAnchors.Count),
BuildConfidence(ctx.MatchedAnchors.Count, operation.RequiredAnchors.Count),

Check failure on line 269 in src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/SwfocTrainer.Runtime/Services/CapabilityMapResolver.cs#L269

Potential null dereference detected.
fingerprint.FingerprintId,
matchedAnchors,
missingAnchors,
ResolveCapabilityMetadata(capabilityHint));
ctx.MatchedAnchors,
ctx.MissingAnchors,
ResolveCapabilityMetadata(ctx.CapabilityHint));
}

private static CapabilityResolutionResult BuildOptionalAnchorsMissingResult(
string requestedProfileId,
string operationId,
string fingerprintId,
IReadOnlyList<string> matchedAnchors,
IReadOnlyList<string> missingAnchors,
CapabilityAvailabilityHint? capabilityHint)
AnchorValidationContext ctx,
string fingerprintId)
{
return new CapabilityResolutionResult(
requestedProfileId,
operationId,
ctx.RequestedProfileId,
ctx.OperationId,
SdkCapabilityStatus.Degraded,
CapabilityReasonCode.OptionalAnchorsMissing,
0.85d,
fingerprintId,
matchedAnchors,
missingAnchors,
ResolveCapabilityMetadata(capabilityHint));
ctx.MatchedAnchors,
ctx.MissingAnchors,
ResolveCapabilityMetadata(ctx.CapabilityHint));
}

private static CapabilityResolutionResult BuildAllRequiredAnchorsPresentResult(
Expand Down Expand Up @@ -507,6 +490,14 @@
public string[]? OptionalAnchors { get; set; } = Array.Empty<string>();
}

private sealed record AnchorValidationContext(
string RequestedProfileId,
string OperationId,
IReadOnlyList<string> MatchedAnchors,
IReadOnlyList<string> MissingAnchors,
CapabilityAvailabilityHint? CapabilityHint);


private sealed class CapabilityEntryDto
{
public string? FeatureId { get; set; } = string.Empty;
Expand Down
Loading
Loading