Files
Tornado3_2026Election/tools/KarismaTcpProbe/Program.cs
2026-04-14 17:14:12 +09:00

1848 lines
81 KiB
C#

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using KAsyncEngineLib;
if (args.Length > 0 && string.Equals(args[0], "--reflect-api", StringComparison.OrdinalIgnoreCase))
{
PrintApiSurface();
return;
}
if (args.Length > 0 && string.Equals(args[0], "--test-counter", StringComparison.OrdinalIgnoreCase))
{
var counterOptions = CounterProbeOptions.Parse(args[1..]);
Console.WriteLine(
$"Karisma counter probe starting. target={counterOptions.Connection.Host}:{counterOptions.Connection.Port} " +
$"scene={counterOptions.ScenePath} object={counterOptions.ObjectName} keyIndex={counterOptions.KeyIndex} value={counterOptions.Number:0.###}");
var counterResult = await ProbeCounterAsync(counterOptions).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine($"- SDK Connect(): {(counterResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
Console.WriteLine($"- Scene Load: {counterResult.SceneLoadOutcome}");
Console.WriteLine($"- Counter Update: {counterResult.CounterOutcome}");
Console.WriteLine($"- Detail: {counterResult.Detail}");
Environment.ExitCode = counterResult.ConnectRequestAccepted &&
counterResult.SceneLoadOutcome == "SUCCESS" &&
counterResult.CounterOutcome == "SUCCESS"
? 0
: 1;
return;
}
if (args.Length > 0 && string.Equals(args[0], "--catalog-scenes", StringComparison.OrdinalIgnoreCase))
{
var catalogOptions = SceneCatalogOptions.Parse(args[1..]);
Console.WriteLine(
$"Karisma scene catalog starting. target={catalogOptions.Connection.Host}:{catalogOptions.Connection.Port} " +
$"root={catalogOptions.RootPath} output={catalogOptions.OutputPath}");
var catalogResult = await CatalogScenesAsync(catalogOptions).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine($"- SDK Connect(): {(catalogResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
Console.WriteLine($"- Scenes Found: {catalogResult.SceneCount}");
Console.WriteLine($"- Scenes Cataloged: {catalogResult.CatalogedCount}");
Console.WriteLine($"- Failures: {catalogResult.Failures.Count}");
Console.WriteLine($"- Output: {catalogResult.OutputPath}");
if (catalogResult.Failures.Count > 0)
{
Console.WriteLine("- Failure Details:");
foreach (var failure in catalogResult.Failures.Take(20))
{
Console.WriteLine($" - {failure.ScenePath}: {failure.Reason}");
}
}
Environment.ExitCode = catalogResult.ConnectRequestAccepted && catalogResult.Failures.Count == 0
? 0
: 1;
return;
}
if (args.Length > 0 && string.Equals(args[0], "--validate-scene-values", StringComparison.OrdinalIgnoreCase))
{
var validationOptions = SceneValidationOptions.Parse(args[1..]);
Console.WriteLine(
$"Karisma scene validation starting. target={validationOptions.Connection.Host}:{validationOptions.Connection.Port} " +
$"scene={validationOptions.ScenePath} ops={validationOptions.OperationsPath} output={validationOptions.OutputPath}");
var validationResult = await ValidateSceneOperationsAsync(validationOptions).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine($"- SDK Connect(): {(validationResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
Console.WriteLine($"- Scene Load: {validationResult.SceneLoadOutcome}");
Console.WriteLine($"- Success Count: {validationResult.SuccessCount}");
Console.WriteLine($"- Failure Count: {validationResult.FailureCount}");
Console.WriteLine($"- Output: {validationResult.OutputPath}");
if (!string.IsNullOrWhiteSpace(validationResult.Detail))
{
Console.WriteLine($"- Detail: {validationResult.Detail}");
}
Environment.ExitCode = validationResult.ConnectRequestAccepted &&
validationResult.SceneLoadOutcome == "SUCCESS" &&
validationResult.FailureCount == 0
? 0
: 1;
return;
}
if (args.Length > 0 && string.Equals(args[0], "--inspect-tscn-folder", StringComparison.OrdinalIgnoreCase))
{
var inspectionOptions = FolderInspectionOptions.Parse(args[1..]);
Console.WriteLine(
$"Karisma tscn inspection starting. target={inspectionOptions.Connection.Host}:{inspectionOptions.Connection.Port} " +
$"root={inspectionOptions.RootPath} output={inspectionOptions.OutputPath}");
var inspectionResult = await InspectTscnFolderAsync(inspectionOptions).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine($"- SDK Connect(): {(inspectionResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
Console.WriteLine($"- Scenes Found: {inspectionResult.SceneCount}");
Console.WriteLine($"- Scenes Processed: {inspectionResult.ProcessedSceneCount}");
Console.WriteLine($"- Variables Found: {inspectionResult.DiscoveredVariableCount}");
Console.WriteLine($"- Failures: {inspectionResult.FailureCount}");
Console.WriteLine($"- Output: {inspectionResult.OutputPath}");
if (!string.IsNullOrWhiteSpace(inspectionResult.Detail))
{
Console.WriteLine($"- Detail: {inspectionResult.Detail}");
}
Environment.ExitCode = inspectionResult.ConnectRequestAccepted && inspectionResult.FailureCount == 0
? 0
: 1;
return;
}
var options = ProbeOptions.Parse(args);
Console.WriteLine($"Karisma TCP probe starting. target={options.Host}:{options.Port} timeout={options.Timeout.TotalSeconds:0}s");
var rawTcpOk = await ProbeRawTcpAsync(options).ConfigureAwait(false);
var sdkResult = await ProbeSdkAsync(options).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine($"- Raw TCP: {(rawTcpOk ? "OK" : "FAILED")}");
Console.WriteLine($"- SDK Connect(): {(sdkResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
Console.WriteLine($"- SDK OnConnect: {sdkResult.ConnectOutcome}");
Console.WriteLine($"- SDK Detail: {sdkResult.Detail}");
Environment.ExitCode = rawTcpOk && sdkResult.ConnectRequestAccepted && sdkResult.ConnectOutcome == "SUCCESS"
? 0
: 1;
static void PrintApiSurface()
{
PrintTypeMethods(typeof(IKAObject));
PrintTypeMethods(typeof(KAObject));
PrintTypeMethods(typeof(IKAScene));
PrintTypeMethods(typeof(KAScene));
PrintTypeMethods(typeof(IKAEngine));
PrintTypeMethods(typeof(KAEngine));
PrintTypeMethods(typeof(IKACounter));
PrintTypeMethods(typeof(KACounter));
PrintTypeMethods(typeof(IKAObjectInfos));
PrintTypeMethods(typeof(KAObjectInfos));
PrintStructFields(typeof(sKObjectInfo));
PrintEnumValues(typeof(eKObjectType));
}
static void PrintTypeMethods(Type type)
{
Console.WriteLine(type.FullName);
foreach (var method in type.GetMethods())
{
if (!method.Name.Contains("Counter", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(method.Name, "SetValue", StringComparison.OrdinalIgnoreCase) &&
!method.Name.Contains("GetObject", StringComparison.OrdinalIgnoreCase) &&
!method.Name.Contains("QueryObjectInfos", StringComparison.OrdinalIgnoreCase) &&
!method.Name.Contains("GetCount", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var parameters = string.Join(
", ",
method.GetParameters().Select(parameter => $"{parameter.ParameterType.Name} {parameter.Name}"));
Console.WriteLine($"- {method.ReturnType.Name} {method.Name}({parameters})");
}
Console.WriteLine();
}
static void PrintStructFields(Type type)
{
Console.WriteLine(type.FullName);
foreach (var field in type.GetFields())
{
Console.WriteLine($"- {field.FieldType.Name} {field.Name}");
}
Console.WriteLine();
}
static void PrintEnumValues(Type type)
{
Console.WriteLine(type.FullName);
foreach (var name in Enum.GetNames(type))
{
Console.WriteLine($"- {name}");
}
Console.WriteLine();
}
static async Task<bool> ProbeRawTcpAsync(ProbeOptions options)
{
try
{
using var client = new TcpClient();
using var cts = new CancellationTokenSource(options.Timeout);
await client.ConnectAsync(options.Host, options.Port, cts.Token).ConfigureAwait(false);
Console.WriteLine($"[RAW] Connected local={client.Client.LocalEndPoint} remote={client.Client.RemoteEndPoint}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"[RAW] Failed: {ex.Message}");
return false;
}
}
static Task<CounterProbeResult> ProbeCounterAsync(CounterProbeOptions options)
{
var completion = new TaskCompletionSource<CounterProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var thread = new Thread(() =>
{
try
{
var handler = new ProbeEventHandler();
var engine = (IKAEngine)new KAEngineClass();
Console.WriteLine("[COUNTER] Calling Connect()...");
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
Console.WriteLine($"[COUNTER] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
if (connectRequested == 0)
{
completion.TrySetResult(new CounterProbeResult(false, "NOT_RUN", "NOT_RUN", "Connect() returned 0."));
return;
}
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
{
completion.TrySetResult(new CounterProbeResult(true, "NOT_RUN", "TIMEOUT", "OnConnect timed out."));
return;
}
if (handler.ConnectTask.Result != 0)
{
completion.TrySetResult(
new CounterProbeResult(true, "NOT_RUN", "FAILED", $"OnConnect errorCode={handler.ConnectTask.Result}"));
return;
}
Console.WriteLine("[COUNTER] Loading scene...");
var scene = engine.LoadScene(options.ScenePath, options.SceneAlias);
if (scene is null)
{
completion.TrySetResult(new CounterProbeResult(true, "FAILED", "NOT_RUN", "LoadScene returned null."));
return;
}
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
{
completion.TrySetResult(new CounterProbeResult(true, "TIMEOUT", "NOT_RUN", "OnLoadScene timed out."));
return;
}
var loadSceneResult = handler.LoadSceneTask.Result;
if (loadSceneResult != eKResult.RESULT_SUCCESS)
{
completion.TrySetResult(
new CounterProbeResult(true, loadSceneResult.ToString(), "NOT_RUN", $"OnLoadScene result={loadSceneResult}"));
return;
}
Console.WriteLine("[COUNTER] Resolving object...");
var sceneObject = scene.GetObject(options.ObjectName);
if (sceneObject is not IKACounter counter)
{
completion.TrySetResult(
new CounterProbeResult(true, "SUCCESS", "FAILED", $"Object '{options.ObjectName}' is not an IKACounter."));
return;
}
Console.WriteLine("[COUNTER] Calling SetCounterNumberKey()...");
counter.SetCounterNumberKey(options.KeyIndex, options.Number);
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
{
completion.TrySetResult(
new CounterProbeResult(true, "SUCCESS", "TIMEOUT", "OnSetCounterNumberKey timed out."));
return;
}
var counterResult = handler.CounterNumberKeyTask.Result;
completion.TrySetResult(
new CounterProbeResult(
true,
"SUCCESS",
counterResult == eKResult.RESULT_SUCCESS ? "SUCCESS" : counterResult.ToString(),
$"OnSetCounterNumberKey result={counterResult}"));
try
{
engine.Disconnect();
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
}
catch (Exception ex)
{
completion.TrySetResult(new CounterProbeResult(false, "EXCEPTION", "EXCEPTION", ex.ToString()));
}
})
{
IsBackground = true,
Name = "KarismaCounterProbe"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completion.Task;
}
static Task<SceneCatalogProbeResult> CatalogScenesAsync(SceneCatalogOptions options)
{
var completion = new TaskCompletionSource<SceneCatalogProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var thread = new Thread(() =>
{
var entries = new List<SceneCatalogEntry>();
var failures = new List<SceneCatalogFailure>();
try
{
var handler = new ProbeEventHandler();
var engine = (IKAEngine)new KAEngineClass();
Console.WriteLine("[CATALOG] Calling Connect()...");
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
Console.WriteLine($"[CATALOG] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
if (connectRequested == 0)
{
completion.TrySetResult(new SceneCatalogProbeResult(false, 0, 0, options.OutputPath, failures));
return;
}
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
{
failures.Add(new SceneCatalogFailure("(connect)", "OnConnect timed out."));
completion.TrySetResult(new SceneCatalogProbeResult(true, 0, 0, options.OutputPath, failures));
return;
}
if (handler.ConnectTask.Result != 0)
{
failures.Add(new SceneCatalogFailure("(connect)", $"OnConnect errorCode={handler.ConnectTask.Result}"));
completion.TrySetResult(new SceneCatalogProbeResult(true, 0, 0, options.OutputPath, failures));
return;
}
var scenePaths = Directory
.EnumerateFiles(options.RootPath, "*.tscn", SearchOption.AllDirectories)
.Where(path => string.IsNullOrWhiteSpace(options.SceneFilter) ||
path.Contains(options.SceneFilter, StringComparison.OrdinalIgnoreCase))
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.Take(options.MaxScenes ?? int.MaxValue)
.ToArray();
for (var index = 0; index < scenePaths.Length; index++)
{
var scenePath = scenePaths[index];
var sceneAlias = $"catalog_{index:D4}";
Console.WriteLine($"[CATALOG] ({index + 1}/{scenePaths.Length}) {scenePath}");
try
{
handler.ResetLoadSceneTask();
handler.ResetObjectInfosTask();
var scene = engine.LoadScene(scenePath, sceneAlias);
if (scene is null)
{
failures.Add(new SceneCatalogFailure(scenePath, "LoadScene returned null."));
continue;
}
handler.ConfigureQueryObjectInfosOnLoad(scene);
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
{
failures.Add(new SceneCatalogFailure(scenePath, "OnLoadScene timed out."));
continue;
}
if (handler.LoadSceneTask.Result != eKResult.RESULT_SUCCESS)
{
failures.Add(new SceneCatalogFailure(scenePath, $"OnLoadScene result={handler.LoadSceneTask.Result}"));
TryUnloadScene(handler, scene, options.Connection.Timeout);
continue;
}
if (!WaitForTaskWithMessagePump(handler.ObjectInfosTask, options.Connection.Timeout))
{
failures.Add(new SceneCatalogFailure(scenePath, "OnQueryObjectInfos timed out."));
TryUnloadScene(handler, scene, options.Connection.Timeout);
continue;
}
var objectInfosResult = handler.ObjectInfosTask.Result;
if (objectInfosResult.Result != eKResult.RESULT_SUCCESS)
{
var reason = string.IsNullOrWhiteSpace(objectInfosResult.Detail)
? $"OnQueryObjectInfos result={objectInfosResult.Result}"
: $"OnQueryObjectInfos result={objectInfosResult.Result} detail={objectInfosResult.Detail}";
failures.Add(new SceneCatalogFailure(scenePath, reason));
TryUnloadScene(handler, scene, options.Connection.Timeout);
continue;
}
entries.Add(new SceneCatalogEntry(
scenePath,
Path.GetRelativePath(options.RootPath, scenePath),
objectInfosResult.Objects));
TryUnloadScene(handler, scene, options.Connection.Timeout);
}
catch (Exception ex)
{
failures.Add(new SceneCatalogFailure(scenePath, ex.Message));
}
}
WriteCatalogMarkdown(options, entries, failures);
completion.TrySetResult(new SceneCatalogProbeResult(true, scenePaths.Length, entries.Count, options.OutputPath, failures));
try
{
engine.Disconnect();
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
}
catch (Exception ex)
{
failures.Add(new SceneCatalogFailure("(exception)", ex.ToString()));
completion.TrySetResult(new SceneCatalogProbeResult(false, entries.Count + failures.Count, entries.Count, options.OutputPath, failures));
}
})
{
IsBackground = true,
Name = "KarismaSceneCatalog"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completion.Task;
}
static Task<SceneValidationProbeResult> ValidateSceneOperationsAsync(SceneValidationOptions options)
{
var completion = new TaskCompletionSource<SceneValidationProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var operations = LoadValidationOperations(options);
var thread = new Thread(() =>
{
try
{
var handler = new ProbeEventHandler();
var engine = (IKAEngine)new KAEngineClass();
var results = new List<SceneOperationValidationResult>();
Console.WriteLine("[VALIDATE] Calling Connect()...");
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
Console.WriteLine($"[VALIDATE] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
if (connectRequested == 0)
{
completion.TrySetResult(new SceneValidationProbeResult(false, "NOT_RUN", 0, operations.Count, options.OutputPath, "Connect() returned 0."));
return;
}
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
{
completion.TrySetResult(new SceneValidationProbeResult(true, "NOT_RUN", 0, operations.Count, options.OutputPath, "OnConnect timed out."));
return;
}
if (handler.ConnectTask.Result != 0)
{
completion.TrySetResult(new SceneValidationProbeResult(true, "NOT_RUN", 0, operations.Count, options.OutputPath, $"OnConnect errorCode={handler.ConnectTask.Result}"));
return;
}
handler.ResetLoadSceneTask();
var scene = engine.LoadScene(options.ScenePath, options.SceneAlias);
if (scene is null)
{
completion.TrySetResult(new SceneValidationProbeResult(true, "FAILED", 0, operations.Count, options.OutputPath, "LoadScene returned null."));
return;
}
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
{
completion.TrySetResult(new SceneValidationProbeResult(true, "TIMEOUT", 0, operations.Count, options.OutputPath, "OnLoadScene timed out."));
return;
}
var loadSceneResult = handler.LoadSceneTask.Result;
if (loadSceneResult != eKResult.RESULT_SUCCESS)
{
completion.TrySetResult(
new SceneValidationProbeResult(true, loadSceneResult.ToString(), 0, operations.Count, options.OutputPath, $"OnLoadScene result={loadSceneResult}"));
return;
}
foreach (var operation in operations)
{
Console.WriteLine($"[VALIDATE] {operation.Method} object={operation.ObjectName}");
var sceneObject = scene.GetObject(operation.ObjectName);
if (sceneObject is null)
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"OBJECT_NOT_FOUND",
"scene.GetObject returned null."));
continue;
}
if (string.Equals(operation.Method, "SetCounterNumberKey", StringComparison.OrdinalIgnoreCase))
{
if (sceneObject is not IKACounter counter)
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"NOT_A_COUNTER",
"Object does not implement IKACounter."));
continue;
}
handler.ResetCounterNumberKeyTask();
counter.SetCounterNumberKey(operation.KeyIndex, operation.Number);
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetCounterNumberKey timed out."));
continue;
}
var callbackResult = handler.CounterNumberKeyTask.Result;
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
callbackResult.ToString(),
string.Empty));
continue;
}
handler.ResetSetValueTask();
sceneObject.SetValue(operation.Value ?? string.Empty);
if (!WaitForTaskWithMessagePump(handler.SetValueTask, options.Connection.Timeout))
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetValue timed out."));
continue;
}
var setValueResult = handler.SetValueTask.Result;
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
setValueResult.ToString(),
string.Empty));
}
WriteSceneValidationMarkdown(options, results);
try
{
scene.UnloadScene();
}
catch
{
}
try
{
engine.Disconnect();
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
completion.TrySetResult(new SceneValidationProbeResult(
true,
"SUCCESS",
results.Count(result => string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal)),
results.Count(result => !string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal)),
options.OutputPath,
string.Empty));
}
catch (Exception ex)
{
completion.TrySetResult(new SceneValidationProbeResult(false, "EXCEPTION", 0, operations.Count, options.OutputPath, ex.ToString()));
}
})
{
IsBackground = true,
Name = "KarismaSceneValidator"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completion.Task;
}
static Task<FolderInspectionProbeResult> InspectTscnFolderAsync(FolderInspectionOptions options)
{
var completion = new TaskCompletionSource<FolderInspectionProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var thread = new Thread(() =>
{
try
{
var handler = new ProbeEventHandler();
var engine = (IKAEngine)new KAEngineClass();
var sceneResults = new List<SceneInspectionResult>();
var failures = new List<SceneCatalogFailure>();
var imagePngPath = FindFirstFile(options.RootPath, "*.png");
var imageVrvPath = FindFirstFile(options.RootPath, "*.vrv");
Console.WriteLine($"[INSPECT] Sample PNG={(imagePngPath ?? "(none)")}");
Console.WriteLine($"[INSPECT] Sample VRV={(imageVrvPath ?? "(none)")}");
Console.WriteLine("[INSPECT] Calling Connect()...");
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
Console.WriteLine($"[INSPECT] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
if (connectRequested == 0)
{
completion.TrySetResult(new FolderInspectionProbeResult(false, 0, 0, 0, 1, options.OutputPath, "Connect() returned 0."));
return;
}
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
{
completion.TrySetResult(new FolderInspectionProbeResult(true, 0, 0, 0, 1, options.OutputPath, "OnConnect timed out."));
return;
}
if (handler.ConnectTask.Result != 0)
{
completion.TrySetResult(new FolderInspectionProbeResult(true, 0, 0, 0, 1, options.OutputPath, $"OnConnect errorCode={handler.ConnectTask.Result}"));
return;
}
var scenePaths = Directory
.EnumerateFiles(options.RootPath, "*.tscn", SearchOption.AllDirectories)
.Where(path => string.IsNullOrWhiteSpace(options.SceneFilter) ||
path.Contains(options.SceneFilter, StringComparison.OrdinalIgnoreCase))
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.Take(options.MaxScenes ?? int.MaxValue)
.ToArray();
for (var sceneIndex = 0; sceneIndex < scenePaths.Length; sceneIndex++)
{
var scenePath = scenePaths[sceneIndex];
var sceneAlias = $"inspect_{sceneIndex:D4}";
Console.WriteLine($"[INSPECT] ({sceneIndex + 1}/{scenePaths.Length}) {scenePath}");
try
{
handler.ResetLoadSceneTask();
var scene = engine.LoadScene(scenePath, sceneAlias);
if (scene is null)
{
failures.Add(new SceneCatalogFailure(scenePath, "LoadScene returned null."));
continue;
}
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
{
failures.Add(new SceneCatalogFailure(scenePath, "OnLoadScene timed out."));
TryUnloadScene(handler, scene, options.Connection.Timeout);
continue;
}
if (handler.LoadSceneTask.Result != eKResult.RESULT_SUCCESS)
{
failures.Add(new SceneCatalogFailure(scenePath, $"OnLoadScene result={handler.LoadSceneTask.Result}"));
TryUnloadScene(handler, scene, options.Connection.Timeout);
continue;
}
var candidates = ExtractCandidateNames(scenePath);
var discoveries = new List<SceneVariableDiscovery>();
foreach (var candidate in candidates)
{
KAObject? sceneObject;
try
{
sceneObject = scene.GetObject(candidate);
}
catch
{
continue;
}
if (sceneObject is null)
{
continue;
}
var textAttempt = TrySetValueOnObject(handler, sceneObject, "__TCP_VALIDATE__", options.Connection.Timeout);
if (textAttempt == eKResult.RESULT_SUCCESS)
{
discoveries.Add(new SceneVariableDiscovery(candidate, "SetValue", "__TCP_VALIDATE__", textAttempt.ToString()));
continue;
}
if (textAttempt == eKResult.RESULT_ERROR_NO_VARIABLE_OBJECT)
{
continue;
}
if (!string.IsNullOrWhiteSpace(imagePngPath))
{
var pngAttempt = TrySetValueOnObject(handler, sceneObject, imagePngPath, options.Connection.Timeout);
if (pngAttempt == eKResult.RESULT_SUCCESS)
{
discoveries.Add(new SceneVariableDiscovery(candidate, "SetValue", imagePngPath, pngAttempt.ToString()));
continue;
}
}
if (!string.IsNullOrWhiteSpace(imageVrvPath))
{
var vrvAttempt = TrySetValueOnObject(handler, sceneObject, imageVrvPath, options.Connection.Timeout);
if (vrvAttempt == eKResult.RESULT_SUCCESS)
{
discoveries.Add(new SceneVariableDiscovery(candidate, "SetValue", imageVrvPath, vrvAttempt.ToString()));
continue;
}
}
if (sceneObject is IKACounter counter)
{
handler.ResetCounterNumberKeyTask();
counter.SetCounterNumberKey(1, 1d);
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
{
continue;
}
var counterResult = handler.CounterNumberKeyTask.Result;
if (counterResult == eKResult.RESULT_SUCCESS)
{
discoveries.Add(new SceneVariableDiscovery(
candidate,
"SetCounterNumberKey",
"keyIndex=1, number=1",
counterResult.ToString()));
}
}
}
sceneResults.Add(new SceneInspectionResult(
scenePath,
Path.GetRelativePath(options.RootPath, scenePath),
candidates.Count,
discoveries.OrderBy(discovery => discovery.VariableName, StringComparer.Ordinal).ToArray()));
TryUnloadScene(handler, scene, options.Connection.Timeout);
}
catch (Exception ex)
{
failures.Add(new SceneCatalogFailure(scenePath, ex.Message));
}
}
WriteFolderInspectionMarkdown(options, sceneResults, failures);
try
{
engine.Disconnect();
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
completion.TrySetResult(new FolderInspectionProbeResult(
true,
scenePaths.Length,
sceneResults.Count,
sceneResults.Sum(result => result.Discoveries.Count),
failures.Count,
options.OutputPath,
string.Empty));
}
catch (Exception ex)
{
completion.TrySetResult(new FolderInspectionProbeResult(false, 0, 0, 0, 1, options.OutputPath, ex.ToString()));
}
})
{
IsBackground = true,
Name = "KarismaTscnInspector"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completion.Task;
}
static eKResult TrySetValueOnObject(ProbeEventHandler handler, KAObject sceneObject, string payload, TimeSpan timeout)
{
handler.ResetSetValueTask();
sceneObject.SetValue(payload);
return WaitForTaskWithMessagePump(handler.SetValueTask, timeout)
? handler.SetValueTask.Result
: eKResult.RESULT_FAILURE;
}
static string? FindFirstFile(string rootPath, string pattern)
{
try
{
return Directory.EnumerateFiles(rootPath, pattern, SearchOption.AllDirectories)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.FirstOrDefault();
}
catch
{
return null;
}
}
static IReadOnlyList<string> ExtractCandidateNames(string scenePath)
{
var data = File.ReadAllBytes(scenePath);
var candidates = new HashSet<string>(StringComparer.Ordinal);
for (var index = 0; index + 8 <= data.Length; index++)
{
var length = BitConverter.ToInt32(data, index);
if (length < 2 || length > 48)
{
continue;
}
var byteLength = length * 2;
var start = index + 4;
var end = start + byteLength;
if (end > data.Length)
{
continue;
}
string value;
try
{
value = System.Text.Encoding.Unicode.GetString(data, start, byteLength);
}
catch
{
continue;
}
if (IsCandidateName(value))
{
candidates.Add(value);
}
}
return candidates.OrderBy(value => value, StringComparer.Ordinal).ToArray();
}
static bool IsCandidateName(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
var trimmed = value.Trim();
if (trimmed.Length < 2 || trimmed.Length > 48)
{
return false;
}
if (trimmed.Contains('\\') ||
trimmed.Contains('/') ||
trimmed.Contains(':') ||
trimmed.Contains('.') ||
trimmed.Contains(' '))
{
return false;
}
if (trimmed.All(char.IsDigit))
{
return false;
}
if (trimmed.StartsWith("OBJECT_TYPE_", StringComparison.Ordinal) ||
trimmed.StartsWith("RESULT_", StringComparison.Ordinal) ||
trimmed.StartsWith("EFFECT_", StringComparison.Ordinal))
{
return false;
}
if (trimmed is "Tornado3" or "Out" or "Default" or "Image" or "Text" or "Counter" or
"Rect" or "Paragraph" or "Char" or "Arial" or "Path" or "Group" or "Change")
{
return false;
}
return trimmed.All(ch =>
ch is >= '0' and <= '9' ||
ch is >= 'A' and <= 'Z' ||
ch is >= 'a' and <= 'z' ||
ch is >= '\uAC00' and <= '\uD7A3' ||
ch is '_' or '-');
}
static void WriteFolderInspectionMarkdown(
FolderInspectionOptions options,
IReadOnlyList<SceneInspectionResult> sceneResults,
IReadOnlyList<SceneCatalogFailure> failures)
{
Directory.CreateDirectory(Path.GetDirectoryName(options.OutputPath)!);
using var writer = new StreamWriter(options.OutputPath, false, new System.Text.UTF8Encoding(false));
writer.WriteLine("# TSCN Variable Discovery");
writer.WriteLine();
writer.WriteLine($"- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine($"- Root: `{options.RootPath}`");
writer.WriteLine($"- Scene Count: {sceneResults.Count}");
writer.WriteLine($"- Discovered Variable Count: {sceneResults.Sum(result => result.Discoveries.Count)}");
writer.WriteLine($"- Failure Count: {failures.Count}");
writer.WriteLine();
writer.WriteLine("## Method");
writer.WriteLine();
writer.WriteLine("- Candidate names are extracted from each `.tscn` as UTF-16LE strings.");
writer.WriteLine("- Each candidate is verified through Karisma TCP callbacks.");
writer.WriteLine("- `SetValue(__TCP_VALIDATE__)`, valid `.png`, valid `.vrv`, and `SetCounterNumberKey(1, 1)` are tried as applicable.");
writer.WriteLine("- Only callbacks that returned `RESULT_SUCCESS` are listed as discovered variables.");
writer.WriteLine();
if (failures.Count > 0)
{
writer.WriteLine("## Failures");
writer.WriteLine();
foreach (var failure in failures)
{
writer.WriteLine($"- `{failure.ScenePath}`: {EscapeInline(failure.Reason)}");
}
writer.WriteLine();
}
writer.WriteLine("## Scenes");
writer.WriteLine();
foreach (var sceneResult in sceneResults)
{
writer.WriteLine($"### `{sceneResult.RelativePath}`");
writer.WriteLine();
writer.WriteLine($"- Candidate Count: {sceneResult.CandidateCount}");
writer.WriteLine($"- Discovered Variables: {sceneResult.Discoveries.Count}");
writer.WriteLine();
if (sceneResult.Discoveries.Count == 0)
{
writer.WriteLine("- No variables discovered with the current TCP validation heuristics.");
writer.WriteLine();
continue;
}
writer.WriteLine("| Variable | Method | Payload | Result |");
writer.WriteLine("| --- | --- | --- | --- |");
foreach (var discovery in sceneResult.Discoveries)
{
writer.WriteLine(
$"| {EscapeCell(discovery.VariableName)} | {EscapeCell(discovery.Method)} | {EscapeCell(discovery.Payload)} | {EscapeCell(discovery.Result)} |");
}
writer.WriteLine();
}
}
static List<SceneValidationOperation> LoadValidationOperations(SceneValidationOptions options)
{
var json = File.ReadAllText(options.OperationsPath);
var operations = JsonSerializer.Deserialize<List<SceneValidationOperation>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
}) ?? new List<SceneValidationOperation>();
foreach (var operation in operations)
{
if (!string.IsNullOrWhiteSpace(operation.Value))
{
operation.Value = operation.Value.Replace("${SCENE_DIR}", Path.GetDirectoryName(options.ScenePath) ?? string.Empty, StringComparison.Ordinal);
}
}
return operations;
}
static string DescribeOperationPayload(SceneValidationOperation operation)
{
return string.Equals(operation.Method, "SetCounterNumberKey", StringComparison.OrdinalIgnoreCase)
? $"keyIndex={operation.KeyIndex}, number={operation.Number:0.###}"
: operation.Value ?? string.Empty;
}
static void WriteSceneValidationMarkdown(SceneValidationOptions options, IReadOnlyList<SceneOperationValidationResult> results)
{
Directory.CreateDirectory(Path.GetDirectoryName(options.OutputPath)!);
using var writer = new StreamWriter(options.OutputPath, false, new System.Text.UTF8Encoding(false));
writer.WriteLine("# Scene Variable Validation");
writer.WriteLine();
writer.WriteLine($"- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine($"- Scene: `{options.ScenePath}`");
writer.WriteLine($"- Operations: `{options.OperationsPath}`");
writer.WriteLine($"- Success Count: {results.Count(result => string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal))}");
writer.WriteLine($"- Failure Count: {results.Count(result => !string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal))}");
writer.WriteLine();
writer.WriteLine("| Object | Method | Payload | Result | Detail |");
writer.WriteLine("| --- | --- | --- | --- | --- |");
foreach (var result in results)
{
writer.WriteLine(
$"| {EscapeCell(result.ObjectName)} | {EscapeCell(result.Method)} | {EscapeCell(result.Payload)} | {EscapeCell(result.Result)} | {EscapeCell(result.Detail)} |");
}
}
static void TryUnloadScene(ProbeEventHandler handler, IKAScene scene, TimeSpan timeout)
{
try
{
handler.ResetUnloadSceneTask();
scene.UnloadScene();
WaitForTaskWithMessagePump(handler.UnloadSceneTask, timeout);
}
catch
{
}
}
static void WriteCatalogMarkdown(
SceneCatalogOptions options,
IReadOnlyList<SceneCatalogEntry> entries,
IReadOnlyList<SceneCatalogFailure> failures)
{
Directory.CreateDirectory(Path.GetDirectoryName(options.OutputPath)!);
using var writer = new StreamWriter(options.OutputPath, false, new System.Text.UTF8Encoding(false));
writer.WriteLine("# Scene Object Catalog");
writer.WriteLine();
writer.WriteLine($"- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine($"- Root: `{options.RootPath}`");
writer.WriteLine($"- Scene Count: {entries.Count}");
writer.WriteLine($"- Failure Count: {failures.Count}");
writer.WriteLine();
if (failures.Count > 0)
{
writer.WriteLine("## Failures");
writer.WriteLine();
foreach (var failure in failures)
{
writer.WriteLine($"- `{failure.ScenePath}`: {EscapeInline(failure.Reason)}");
}
writer.WriteLine();
}
writer.WriteLine("## Scenes");
writer.WriteLine();
foreach (var entry in entries)
{
writer.WriteLine($"### `{entry.RelativePath}`");
writer.WriteLine();
writer.WriteLine($"- Object Count: {entry.Objects.Count}");
writer.WriteLine();
writer.WriteLine("| Name | Type | Value | Visible |");
writer.WriteLine("| --- | --- | --- | --- |");
foreach (var obj in entry.Objects)
{
writer.WriteLine(
$"| {EscapeCell(obj.Name)} | {obj.ObjectType} | {EscapeCell(obj.Value)} | {(obj.Visible ? "true" : "false")} |");
}
writer.WriteLine();
}
}
static string EscapeCell(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return value.Replace("\\", "\\\\", StringComparison.Ordinal)
.Replace("|", "\\|", StringComparison.Ordinal)
.Replace("\r", " ", StringComparison.Ordinal)
.Replace("\n", "<br/>", StringComparison.Ordinal);
}
static string EscapeInline(string? value) => EscapeCell(value);
static Task<SdkProbeResult> ProbeSdkAsync(ProbeOptions options)
{
var completion = new TaskCompletionSource<SdkProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var thread = new Thread(() =>
{
try
{
var handler = new ProbeEventHandler();
var engine = (IKAEngine)new KAEngineClass();
Console.WriteLine("[SDK] Calling Connect()...");
var connectRequested = engine.Connect(options.Host, options.Port, handler);
Console.WriteLine($"[SDK] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
if (connectRequested == 0)
{
completion.TrySetResult(new SdkProbeResult(false, "FAILED", "Connect() returned 0."));
return;
}
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Timeout))
{
completion.TrySetResult(new SdkProbeResult(true, "TIMEOUT", $"OnConnect was not received within {options.Timeout.TotalSeconds:0} seconds."));
return;
}
var errorCode = handler.ConnectTask.Result;
var outcome = errorCode == 0 ? "SUCCESS" : "FAILED";
completion.TrySetResult(new SdkProbeResult(true, outcome, $"OnConnect errorCode={errorCode}"));
try
{
engine.Disconnect();
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
}
catch (Exception ex)
{
completion.TrySetResult(new SdkProbeResult(false, "EXCEPTION", ex.ToString()));
}
})
{
IsBackground = true,
Name = "KarismaTcpProbe"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completion.Task;
}
static bool WaitForTaskWithMessagePump(Task task, TimeSpan timeout)
{
User32.PeekMessage(out _, IntPtr.Zero, 0, 0, 0);
var deadline = DateTime.UtcNow + timeout;
while (!task.IsCompleted)
{
while (User32.PeekMessage(out var message, IntPtr.Zero, 0, 0, 1))
{
User32.TranslateMessage(ref message);
User32.DispatchMessage(ref message);
}
if (DateTime.UtcNow >= deadline)
{
return false;
}
Thread.Sleep(10);
}
return true;
}
internal sealed record ProbeOptions(string Host, int Port, TimeSpan Timeout)
{
public static ProbeOptions Parse(string[] args)
{
var host = "127.0.0.1";
var port = 30001;
var timeout = TimeSpan.FromSeconds(5);
for (var index = 0; index < args.Length; index++)
{
switch (args[index])
{
case "--host" when index + 1 < args.Length:
host = args[++index];
break;
case "--port" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedPort):
port = parsedPort;
index++;
break;
case "--timeout" when index + 1 < args.Length && double.TryParse(args[index + 1], out var parsedTimeoutSeconds):
timeout = TimeSpan.FromSeconds(parsedTimeoutSeconds);
index++;
break;
}
}
return new ProbeOptions(host, port, timeout);
}
}
internal sealed record CounterProbeOptions(
ProbeOptions Connection,
string ScenePath,
string SceneAlias,
string ObjectName,
int KeyIndex,
double Number)
{
public static CounterProbeOptions Parse(string[] args)
{
var connection = ProbeOptions.Parse(args);
string? scenePath = null;
string? sceneAlias = null;
string? objectName = null;
var keyIndex = 1;
var number = 0d;
for (var index = 0; index < args.Length; index++)
{
switch (args[index])
{
case "--scene" when index + 1 < args.Length:
scenePath = args[++index];
break;
case "--alias" when index + 1 < args.Length:
sceneAlias = args[++index];
break;
case "--object" when index + 1 < args.Length:
objectName = args[++index];
break;
case "--key-index" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedKeyIndex):
keyIndex = parsedKeyIndex;
index++;
break;
case "--number" when index + 1 < args.Length && double.TryParse(args[index + 1], out var parsedNumber):
number = parsedNumber;
index++;
break;
}
}
if (string.IsNullOrWhiteSpace(scenePath))
{
throw new ArgumentException("--scene is required.");
}
if (string.IsNullOrWhiteSpace(objectName))
{
throw new ArgumentException("--object is required.");
}
sceneAlias ??= Path.GetFileNameWithoutExtension(scenePath);
return new CounterProbeOptions(connection, scenePath, sceneAlias, objectName, keyIndex, number);
}
}
internal sealed record SceneCatalogOptions(
ProbeOptions Connection,
string RootPath,
string OutputPath,
string? SceneFilter,
int? MaxScenes)
{
public static SceneCatalogOptions Parse(string[] args)
{
var connection = ProbeOptions.Parse(args);
string? rootPath = null;
string? outputPath = null;
string? sceneFilter = null;
int? maxScenes = null;
for (var index = 0; index < args.Length; index++)
{
switch (args[index])
{
case "--root" when index + 1 < args.Length:
rootPath = args[++index];
break;
case "--output" when index + 1 < args.Length:
outputPath = args[++index];
break;
case "--filter" when index + 1 < args.Length:
sceneFilter = args[++index];
break;
case "--max-scenes" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedMaxScenes):
maxScenes = parsedMaxScenes;
index++;
break;
}
}
rootPath ??= Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"Tornado3 Data",
"T3_Cut",
"T3_Cut");
if (!Directory.Exists(rootPath))
{
throw new DirectoryNotFoundException($"Catalog root path does not exist: {rootPath}");
}
outputPath ??= Path.Combine(Environment.CurrentDirectory, "SCENE_OBJECT_CATALOG.md");
outputPath = Path.GetFullPath(outputPath);
return new SceneCatalogOptions(connection, Path.GetFullPath(rootPath), outputPath, sceneFilter, maxScenes);
}
}
internal sealed record SceneValidationOptions(
ProbeOptions Connection,
string ScenePath,
string SceneAlias,
string OperationsPath,
string OutputPath)
{
public static SceneValidationOptions Parse(string[] args)
{
var connection = ProbeOptions.Parse(args);
string? scenePath = null;
string? sceneAlias = null;
string? operationsPath = null;
string? outputPath = null;
for (var index = 0; index < args.Length; index++)
{
switch (args[index])
{
case "--scene" when index + 1 < args.Length:
scenePath = args[++index];
break;
case "--alias" when index + 1 < args.Length:
sceneAlias = args[++index];
break;
case "--operations" when index + 1 < args.Length:
operationsPath = args[++index];
break;
case "--output" when index + 1 < args.Length:
outputPath = args[++index];
break;
}
}
if (string.IsNullOrWhiteSpace(scenePath))
{
throw new ArgumentException("--scene is required.");
}
if (string.IsNullOrWhiteSpace(operationsPath))
{
throw new ArgumentException("--operations is required.");
}
scenePath = Path.GetFullPath(scenePath);
operationsPath = Path.GetFullPath(operationsPath);
sceneAlias ??= Path.GetFileNameWithoutExtension(scenePath);
outputPath ??= Path.Combine(Environment.CurrentDirectory, "SCENE_VARIABLE_VALIDATION.md");
outputPath = Path.GetFullPath(outputPath);
return new SceneValidationOptions(connection, scenePath, sceneAlias, operationsPath, outputPath);
}
}
internal sealed record FolderInspectionOptions(ProbeOptions Connection, string RootPath, string OutputPath, string? SceneFilter, int? MaxScenes)
{
public static FolderInspectionOptions Parse(string[] args)
{
var connection = ProbeOptions.Parse(args);
string? rootPath = null;
string? outputPath = null;
string? sceneFilter = null;
int? maxScenes = null;
for (var index = 0; index < args.Length; index++)
{
switch (args[index])
{
case "--root" when index + 1 < args.Length:
rootPath = args[++index];
break;
case "--output" when index + 1 < args.Length:
outputPath = args[++index];
break;
case "--filter" when index + 1 < args.Length:
sceneFilter = args[++index];
break;
case "--max-scenes" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedMaxScenes):
maxScenes = parsedMaxScenes;
index++;
break;
}
}
if (string.IsNullOrWhiteSpace(rootPath))
{
throw new ArgumentException("--root is required.");
}
rootPath = Path.GetFullPath(rootPath);
outputPath ??= Path.Combine(Environment.CurrentDirectory, "TSCN_VARIABLE_DISCOVERY.md");
outputPath = Path.GetFullPath(outputPath);
return new FolderInspectionOptions(connection, rootPath, outputPath, sceneFilter, maxScenes);
}
}
internal sealed record SdkProbeResult(bool ConnectRequestAccepted, string ConnectOutcome, string Detail);
internal sealed record CounterProbeResult(bool ConnectRequestAccepted, string SceneLoadOutcome, string CounterOutcome, string Detail);
internal sealed record SceneCatalogProbeResult(
bool ConnectRequestAccepted,
int SceneCount,
int CatalogedCount,
string OutputPath,
IReadOnlyList<SceneCatalogFailure> Failures);
internal sealed record SceneCatalogFailure(string ScenePath, string Reason);
internal sealed record SceneCatalogEntry(string ScenePath, string RelativePath, IReadOnlyList<SceneObjectCatalogItem> Objects);
internal sealed record SceneObjectCatalogItem(string Name, eKObjectType ObjectType, string Value, bool Visible);
internal sealed record ObjectInfosProbeResult(eKResult Result, string SceneName, IReadOnlyList<SceneObjectCatalogItem> Objects, string Detail);
internal sealed record SceneValidationProbeResult(
bool ConnectRequestAccepted,
string SceneLoadOutcome,
int SuccessCount,
int FailureCount,
string OutputPath,
string Detail);
internal sealed record FolderInspectionProbeResult(
bool ConnectRequestAccepted,
int SceneCount,
int ProcessedSceneCount,
int DiscoveredVariableCount,
int FailureCount,
string OutputPath,
string Detail);
internal sealed class SceneValidationOperation
{
public string ObjectName { get; set; } = string.Empty;
public string Method { get; set; } = "SetValue";
public string? Value { get; set; }
public int KeyIndex { get; set; }
public double Number { get; set; }
}
internal sealed record SceneOperationValidationResult(string ObjectName, string Method, string Payload, string Result, string Detail);
internal sealed record SceneInspectionResult(string ScenePath, string RelativePath, int CandidateCount, IReadOnlyList<SceneVariableDiscovery> Discoveries);
internal sealed record SceneVariableDiscovery(string VariableName, string Method, string Payload, string Result);
internal sealed class ProbeEventHandler : KAEventHandler
{
private readonly TaskCompletionSource<int> _connectTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly TaskCompletionSource<int> _closeTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _loadSceneTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _unloadSceneTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _counterNumberKeyTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _setValueTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<ObjectInfosProbeResult> _objectInfosTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private IKAScene? _sceneToQueryOnLoad;
public Task<int> ConnectTask => _connectTask.Task;
public Task<int> CloseTask => _closeTask.Task;
public Task<eKResult> LoadSceneTask => _loadSceneTask.Task;
public Task<eKResult> UnloadSceneTask => _unloadSceneTask.Task;
public Task<eKResult> CounterNumberKeyTask => _counterNumberKeyTask.Task;
public Task<eKResult> SetValueTask => _setValueTask.Task;
public Task<ObjectInfosProbeResult> ObjectInfosTask => _objectInfosTask.Task;
public void ResetLoadSceneTask() => _loadSceneTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ResetUnloadSceneTask() => _unloadSceneTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ResetCounterNumberKeyTask() => _counterNumberKeyTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ResetSetValueTask() => _setValueTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ResetObjectInfosTask() => _objectInfosTask = new TaskCompletionSource<ObjectInfosProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ConfigureQueryObjectInfosOnLoad(IKAScene scene) => _sceneToQueryOnLoad = scene;
public void OnConnect(int ErrorCode)
{
Console.WriteLine($"[SDK] OnConnect errorCode={ErrorCode}");
_connectTask.TrySetResult(ErrorCode);
}
public void OnClose(int ErrorCode)
{
Console.WriteLine($"[SDK] OnClose errorCode={ErrorCode}");
_closeTask.TrySetResult(ErrorCode);
}
public void OnLoadScene(eKResult Result, string SceneName)
{
Console.WriteLine($"[SDK] OnLoadScene result={Result} scene={SceneName}");
_loadSceneTask.TrySetResult(Result);
if (Result == eKResult.RESULT_SUCCESS && _sceneToQueryOnLoad is not null)
{
try
{
_sceneToQueryOnLoad.QueryObjectInfos();
}
catch (Exception ex)
{
_objectInfosTask.TrySetResult(new ObjectInfosProbeResult(
eKResult.RESULT_FAILURE,
SceneName,
Array.Empty<SceneObjectCatalogItem>(),
ex.Message));
}
finally
{
_sceneToQueryOnLoad = null;
}
}
}
public void OnLoadSceneForce(eKResult Result, string SceneName) { }
public void OnLogMessage(string LogMessage) { }
public void OnMessageNo(uint MessageNo) { }
public void OnBeginTransaction(eKResult Result) { }
public void OnEndTransaction(eKResult Result) { }
public void OnHeartBeat(eKResult Result) { }
public void OnUnloadAll(eKResult Result) { }
public void OnSetTrialPlayoutMode(eKResult Result) { }
public void OnCheckVersion(eKResult Result, string ServerVersion, string SDKVersion) { }
public void OnSetAudioOutput(eKResult Result) { }
public void OnScenePrepare(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnScenePrepareEx(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnPlay(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnPlayOut(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnStop(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnStopAll(eKResult Result) { }
public void OnPause(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnResume(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnQueryIsOnAir(eKResult Result, int OutputChannelIndex, int LayerNo, int bOnAir) { }
public void OnTrigger(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnScenePlayingStarted(string SceneName, int OutputChannelIndex, int LayerNo) { }
public void OnScenePlayed(string SceneName, int OutputChannelIndex, int LayerNo) { }
public void OnSceneAnimationPlayed(string SceneName, int OutputChannelIndex, int LayerNo, string AnimationName) { }
public void OnScenePaused(string SceneName, int OutputChannelIndex, int LayerNo, int bLastPause) { }
public void OnSceneSaved(eKResult Result, string FileName) { }
public void OnTriggerObject(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnResumeBackground(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnSaveMixedPreviewImage(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnPlayDirect(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnCutIn(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnCutOut(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnClearNextPreview(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnPlayRange(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnQueryPlaybackRangeCount(eKResult Result, string SceneName, int PlaybackRangeCount) { }
public void OnQueryPlaybackRange(eKResult Result, string SceneName, int PlaybackRangeNo, int Start, int End) { }
public void OnQueryOutputChannelIndex(eKResult Result, string SceneName, int OutputChannelIndex) { }
public void OnPlayInNextPreview(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnPlayOutNextPreview(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnSetBackgroundFill(eKResult Result, string SceneName) { }
public void OnSetBackgroundTexture(eKResult Result, string SceneName) { }
public void OnSetBackgroundVideo(eKResult Result, string SceneName) { }
public void OnSetBackgroundLiveIn(eKResult Result, string SceneName) { }
public void OnUseBackground(eKResult Result, string SceneName) { }
public void OnSetBackgroundVideoPlayInfo(eKResult Result, string SceneName) { }
public void OnQueryBackgroundVideoPlayInfo(eKResult Result, string SceneName, ref sKVideoPlayInfo pVideoPlayInfo) { }
public void OnSetSceneEffectType(eKResult Result, string SceneName) { }
public void OnSaveSceneImage(eKResult Result, string SceneName) { }
public void OnSaveScene(eKResult Result, string SceneName) { }
public void OnUnloadScene(eKResult Result, string SceneName)
{
_unloadSceneTask.TrySetResult(Result);
}
public void OnReloadScene(eKResult Result, string SceneName) { }
public void OnUpdateTextures(eKResult Result, string SceneName) { }
public void OnSetSceneAudioFile(eKResult Result, string SceneName) { }
public void OnEnableSceneAudio(eKResult Result, string SceneName) { }
public void OnSetSceneDuration(eKResult Result, string SceneName) { }
public void OnSetBackgroundPauseType(eKResult Result, string SceneName) { }
public void OnSetBackgroundChangeType(eKResult Result, string SceneName) { }
public void OnSetBackgroundPauseAtZeroFrameAsStandBy(eKResult Result, string SceneName) { }
public void OnResetDuration(eKResult Result, string SceneName) { }
public void OnSetDuration(eKResult Result, string SceneName) { }
public void OnAddObject(eKResult Result, string SceneName) { }
public void OnAddCloneObject(eKResult Result, string SceneName) { }
public void OnUpdateThumbnail(eKResult Result, string SceneName) { }
public void OnExportVideo(eKResult Result, string SceneName) { }
public void OnStopVideoExporting(eKResult Result) { }
public void OnQueryVideoExportingProgress(eKResult Result, string TargetName, int CurrentFrame, int TotalFrame) { }
public void OnFinishedVideoExporting(eKResult Result, string FileName) { }
public void OnAddPause(eKResult Result, string SceneName) { }
public void OnDeletePause(eKResult Result, string SceneName) { }
public void OnSetPause(eKResult Result, string SceneName) { }
public void OnSetPauseWithIndex(eKResult Result, string SceneName) { }
public void OnDeletePauseWithIndex(eKResult Result, string SceneName) { }
public void OnQueryPauseCount(eKResult Result, string SceneName, int PauseCount) { }
public void OnQueryObjectInfos(eKResult Result, string SceneName, KAObjectInfos pObjectInfos)
{
var objects = new List<SceneObjectCatalogItem>();
if (Result == eKResult.RESULT_SUCCESS)
{
var count = pObjectInfos.GetCount();
for (var index = 0; index < count; index++)
{
var objectInfo = pObjectInfos.GetObjectInfo(index);
objects.Add(new SceneObjectCatalogItem(
objectInfo.Name ?? string.Empty,
objectInfo.ObjectType,
objectInfo.Value ?? string.Empty,
objectInfo.bVisible != 0));
}
}
_objectInfosTask.TrySetResult(new ObjectInfosProbeResult(Result, SceneName, objects, string.Empty));
}
public void OnQueryAnimationNames(eKResult Result, string SceneName, KAStrings pAnimationNames) { }
public void OnQueryAnimationCount(eKResult Result, string SceneName, int AnimationCount) { }
public void OnQueryObjectInfosByScreenPoint(eKResult Result, KAObjectInfos pObjectInfos) { }
public void OnQuerySceneEffectType(eKResult Result, string SceneName, int bInEffect, eKEffectType EffectType, int Duration) { }
public void OnQueryDuration(eKResult Result, string SceneName, string AnimationName, int Duration) { }
public void OnQueryContentsOfTextObjects(eKResult Result, string SceneName, KAStrings pTexts) { }
public void OnSetStyleColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetStyleTexture(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetFaceTextColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetEdgeTextColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetShadowTextColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetVisible(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetValue(eKResult Result, string SceneName, string ObjectName)
{
if (Result != eKResult.RESULT_ERROR_NO_VARIABLE_OBJECT)
{
Console.WriteLine($"[SDK] OnSetValue result={Result} scene={SceneName} object={ObjectName}");
}
_setValueTask.TrySetResult(Result);
}
public void OnAddText(eKResult Result, string SceneName, string ObjectName) { }
public void OnStoreTextStyle(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetTextStyle(eKResult Result, string SceneName, string ObjectName) { }
public void OnEditText(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetFont(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetTextRange(eKResult Result, string SceneName, string ObjectName) { }
public void OnResetTextRange(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryObjectType(eKResult Result, string SceneName, string ObjectName, eKObjectType ObjectType) { }
public void OnSetChartCSVFile(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetChartCellData(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryChartDataTable(eKResult Result, string SceneName, string ObjectName, KAChartDataTable Table) { }
public void OnQuerySize(eKResult Result, string SceneName, string ObjectName, float Width, float Height) { }
public void OnSetSize(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCounterNumberKey(eKResult Result, string SceneName, string ObjectName)
{
if (Result != eKResult.RESULT_ERROR_NO_VARIABLE_OBJECT)
{
Console.WriteLine($"[SDK] OnSetCounterNumberKey result={Result} scene={SceneName} object={ObjectName}");
}
_counterNumberKeyTask.TrySetResult(Result);
}
public void OnSetPositionKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetRotationKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetScaleKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCylinderAngleKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetSphereAngleKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCircleAngleKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCropKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCountDown(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetPosition(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetRotation(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetScale(eKResult Result, string SceneName, string ObjectName) { }
public void OnAddPathPoint(eKResult Result, string SceneName, string ObjectName) { }
public void OnClearPathPoints(eKResult Result, string SceneName, string ObjectName) { }
public void OnAddPathShapePoint(eKResult Result, string SceneName, string ObjectName) { }
public void OnClearPathShapePoints(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryScrollRemainingDistance(eKResult Result, string SceneName, string ObjectName, int ScrollRemainingDistance) { }
public void OnQueryScrollChildRemainingDistance(eKResult Result, string SceneName, string ObjectName, string ChildName, int ScrollRemainingDistance) { }
public void OnAddScrollObject(eKResult Result, string SceneName, string ObjectName) { }
public void OnAdjustScrollSpeed(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetScrollSpeed(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetVariableName(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetLoftPositionKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetChangeOut(eKResult Result, string SceneName) { }
public void OnModifyPathPoint(eKResult Result, string SceneName, string ObjectName) { }
public void OnInitScrollObject(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCounterInfo(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCounterNumber(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCounterRange(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCounterRemainingTime(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetCounterElapsedTime(eKResult Result, string SceneName, string ObjectName) { }
public void OnSaveObjectImage(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetPositionOfPathAnimation(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetPositionKeyOfPathAnimation(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetStartFrame(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetObjectEffectType(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetObjectOutEffectDelay(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetColorKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetEmissiveColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetEmissiveColorKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetTransparencyOpacity(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetTransparencyOpacityKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetExposure(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetExposureKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureType(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureFile(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureOffset(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureOffsetKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureTiling(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureTilingKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureRotation(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureRotationKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureOpacity(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureOpacityKey(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryGroupType(eKResult Result, string SceneName, string ObjectName, eKGroupType GroupType) { }
public void OnQueryImageType(eKResult Result, string SceneName, string ObjectName, eKImageType ImageType) { }
public void OnSetVideoPlayInfo(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryVideoPlayInfo(eKResult Result, string SceneName, string ObjectName, ref sKVideoPlayInfo pVideoPlayInfo) { }
public void OnQueryIs3D(eKResult Result, string SceneName, string ObjectName, int b3D) { }
public void OnQueryPosition(eKResult Result, string SceneName, string ObjectName, float X, float Y, float Z) { }
public void OnSetImageType(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMemo(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryMemo(eKResult Result, string SceneName, string ObjectName, string Memo) { }
public void OnQueryFont(eKResult Result, string SceneName, string ObjectName, ref sKFont Param) { }
public void OnSetImageOriginalSize(eKResult Result, string SceneName, string ObjectName) { }
public void OnApplyChangeEffectLibrary(eKResult Result, string SceneName) { }
public void OnApplyObjectLibrary(eKResult Result, string SceneName, string ObjectName) { }
public void OnApplyTextureEffectLibrary(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetTableValue(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetTableColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryTableValue(eKResult Result, string SceneName, string ObjectName, int Row, int Column, string Value) { }
public void OnQueryTableValues(eKResult Result, string SceneName, string ObjectName, KATableValues pValues) { }
public void OnSetPathShapeOutlineThickness(eKResult Result, string SceneName, string ObjectName) { }
public void OnEnablePathShapeOutline(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetPlaybackCamera(eKResult Result, string SceneName) { }
public void OnSetMaterialTextureVideoPlayInfo(eKResult Result, string SceneName, string ObjectName) { }
public void OnQueryMaterialTextureVideoPlayInfo(eKResult Result, string SceneName, string ObjectName, ref sKVideoPlayInfo VideoPlayInfo) { }
public void OnQueryVideoFormat(eKResult Result, ref sKVideoFormat VideoFormat) { }
public void OnQueryLiveStreamingStatus(eKResult Result, string StreamingURI, eKLiveStreamingStatus Status) { }
public void OnPreloadLiveStreaming(eKResult Result, string StreamingURI) { }
public void OnReleaseLiveStreaming(eKResult Result, string StreamingURI) { }
public void OnUpdateImageResource(eKResult Result) { }
public void OnQueryLayerCount(eKResult Result, int LayerCount) { }
public void OnSetLayerViewportRate(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnSetLayerViewportRateEx(eKResult Result, int OutputChannelIndex, int LayerNo) { }
public void OnSetFitting(eKResult Result, string SceneName) { }
public void OnSetFittingOffset(eKResult Result, string SceneName) { }
public void OnSetFittingScale(eKResult Result, string SceneName) { }
public void OnSetLightColor(eKResult Result, string SceneName, string ObjectName) { }
public void OnEnableLight(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetDirectionalLight(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetPointLight(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetSpotLight(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetInfinitePointLight(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetMaterialTextureLiveStreamingURI(eKResult Result, string SceneName, string ObjectName) { }
public void OnSetBackgroundLiveStreamingURI(eKResult Result, string SceneName) { }
public void OnLoadProject(eKResult Result, string FilePath, string AliasName) { }
public void OnNewProject(eKResult Result, string AliasName) { }
public void OnUnloadAllProject(eKResult Result) { }
public void OnSaveProject(eKResult Result, string AliasName) { }
public void OnQuerySceneItemCount(eKResult Result, string AliasName, int SceneItemCount) { }
public void OnQuerySceneItemInfos(eKResult Result, string AliasName, KASceneItemInfos SceneItemInfos) { }
public void OnAddSceneItem(eKResult Result, string AliasName, int Index) { }
public void OnInsertSceneItem(eKResult Result, string AliasName) { }
public void OnDeleteSceneItem(eKResult Result, string AliasNAme) { }
public void OnQueryProjectFormat(eKResult Result, ref sKVideoFormat ProjectFormat) { }
public void OnSetTimecode(eKResult Result, string AliasName) { }
public void OnSetTimecodeInOut(eKResult Result, string AliasName) { }
public void OnSetTimecodeTrack(eKResult Result, string AliasName) { }
public void OnSetTimecodeInOutType(eKResult Result, string AliasName) { }
public void OnDeleteTimecode(eKResult Result, string AliasName) { }
public void OnQueryTimecode(eKResult Result, int TrackNo, int In, int Out, int bOnTrack) { }
public void OnUnloadProject(eKResult Result, string AliasName) { }
public void OnEnableSyncWithSceneEffect(eKResult Result, string AliasName) { }
public void OnExportProjectVideo(eKResult Result, string AliasName) { }
public void OnExportSceneImage(eKResult Result, string SceneName) { }
public void OnStartVideoCapture(eKResult Result) { }
public void OnStopVideoCapture(eKResult Result) { }
public void OnCaptureImage(eKResult Result) { }
}
[StructLayout(LayoutKind.Sequential)]
internal struct NativeMessage
{
public IntPtr hwnd;
public uint message;
public UIntPtr wParam;
public IntPtr lParam;
public uint time;
public NativePoint pt;
public uint lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NativePoint
{
public int x;
public int y;
}
internal static class User32
{
[DllImport("user32.dll")]
internal static extern bool PeekMessage(out NativeMessage lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
[DllImport("user32.dll")]
internal static extern bool TranslateMessage([In] ref NativeMessage lpMsg);
[DllImport("user32.dll")]
internal static extern IntPtr DispatchMessage([In] ref NativeMessage lpMsg);
}