중간저장 04.20

This commit is contained in:
2026-04-20 20:06:18 +09:00
parent 210b546130
commit 7cedeef5a9
180 changed files with 36496 additions and 918 deletions

View File

@@ -119,6 +119,35 @@ if (args.Length > 0 && string.Equals(args[0], "--inspect-tscn-folder", StringCom
return;
}
if (args.Length > 0 && string.Equals(args[0], "--save-scene-image", StringComparison.OrdinalIgnoreCase))
{
var saveImageOptions = SaveSceneImageOptions.Parse(args[1..]);
Console.WriteLine(
$"Karisma scene image save starting. target={saveImageOptions.Connection.Host}:{saveImageOptions.Connection.Port} " +
$"scene={saveImageOptions.ScenePath} output={saveImageOptions.OutputPath} size={saveImageOptions.Width}x{saveImageOptions.Height} frame={saveImageOptions.Frame}");
var saveImageResult = await SaveSceneImageAsync(saveImageOptions).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("Summary");
Console.WriteLine($"- SDK Connect(): {(saveImageResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
Console.WriteLine($"- Scene Load: {saveImageResult.SceneLoadOutcome}");
Console.WriteLine($"- Save Scene Image: {saveImageResult.SaveOutcome}");
Console.WriteLine($"- Output: {saveImageResult.OutputPath}");
Console.WriteLine($"- Detail: {saveImageResult.Detail}");
Environment.ExitCode = saveImageResult.ConnectRequestAccepted &&
saveImageResult.SceneLoadOutcome == "SUCCESS" &&
saveImageResult.SaveOutcome == "SUCCESS"
? 0
: 1;
return;
}
if (args.Length > 0 && string.Equals(args[0], "--validate-live-cuts", StringComparison.OrdinalIgnoreCase))
{
Environment.ExitCode = await LiveCutValidation.RunAsync(args[1..]).ConfigureAwait(false);
return;
}
var options = ProbeOptions.Parse(args);
Console.WriteLine($"Karisma TCP probe starting. target={options.Host}:{options.Port} timeout={options.Timeout.TotalSeconds:0}s");
@@ -321,6 +350,152 @@ static Task<CounterProbeResult> ProbeCounterAsync(CounterProbeOptions options)
return completion.Task;
}
static Task<SaveSceneImageProbeResult> SaveSceneImageAsync(SaveSceneImageOptions options)
{
var completion = new TaskCompletionSource<SaveSceneImageProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var thread = new Thread(() =>
{
ProbeEventHandler? handler = null;
IKAEngine? engine = null;
IKAScene? scene = null;
try
{
handler = new ProbeEventHandler();
engine = (IKAEngine)new KAEngineClass();
Console.WriteLine("[SAVE-IMAGE] Calling Connect()...");
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
Console.WriteLine($"[SAVE-IMAGE] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
if (connectRequested == 0)
{
completion.TrySetResult(new SaveSceneImageProbeResult(false, "NOT_RUN", "NOT_RUN", options.OutputPath, "Connect() returned 0."));
return;
}
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
{
completion.TrySetResult(new SaveSceneImageProbeResult(true, "NOT_RUN", "TIMEOUT", options.OutputPath, "OnConnect timed out."));
return;
}
if (handler.ConnectTask.Result != 0)
{
completion.TrySetResult(
new SaveSceneImageProbeResult(true, "NOT_RUN", "FAILED", options.OutputPath, $"OnConnect errorCode={handler.ConnectTask.Result}"));
return;
}
Console.WriteLine("[SAVE-IMAGE] Loading scene...");
handler.ResetLoadSceneTask();
scene = engine.LoadScene(options.ScenePath, options.SceneAlias);
if (scene is null)
{
completion.TrySetResult(new SaveSceneImageProbeResult(true, "FAILED", "NOT_RUN", options.OutputPath, "LoadScene returned null."));
return;
}
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
{
completion.TrySetResult(new SaveSceneImageProbeResult(true, "TIMEOUT", "NOT_RUN", options.OutputPath, "OnLoadScene timed out."));
return;
}
if (handler.LoadSceneTask.Result != eKResult.RESULT_SUCCESS)
{
completion.TrySetResult(
new SaveSceneImageProbeResult(true, handler.LoadSceneTask.Result.ToString(), "NOT_RUN", options.OutputPath, $"OnLoadScene result={handler.LoadSceneTask.Result}"));
return;
}
var outputDirectory = Path.GetDirectoryName(options.OutputPath);
if (!string.IsNullOrWhiteSpace(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
if (File.Exists(options.OutputPath))
{
File.Delete(options.OutputPath);
}
Console.WriteLine("[SAVE-IMAGE] Calling SaveSceneImage()...");
handler.ResetSaveSceneImageTask();
scene.SaveSceneImage(options.OutputPath, options.Width, options.Height, options.Frame);
if (!WaitForTaskWithMessagePump(handler.SaveSceneImageTask, options.Connection.Timeout))
{
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", options.OutputPath, "OnSaveSceneImage timed out."));
return;
}
var saveResult = handler.SaveSceneImageTask.Result;
if (saveResult != eKResult.RESULT_SUCCESS)
{
completion.TrySetResult(
new SaveSceneImageProbeResult(true, "SUCCESS", saveResult.ToString(), options.OutputPath, $"OnSaveSceneImage result={saveResult}"));
return;
}
var fileWaitDeadline = DateTime.UtcNow + options.Connection.Timeout;
while (DateTime.UtcNow < fileWaitDeadline)
{
if (File.Exists(options.OutputPath))
{
var info = new FileInfo(options.OutputPath);
if (info.Length > 0)
{
completion.TrySetResult(
new SaveSceneImageProbeResult(true, "SUCCESS", "SUCCESS", options.OutputPath, $"Saved {info.Length} bytes."));
return;
}
}
Thread.Sleep(50);
}
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", options.OutputPath, "Image file was not created."));
}
catch (Exception ex)
{
completion.TrySetResult(new SaveSceneImageProbeResult(false, "EXCEPTION", "EXCEPTION", options.OutputPath, ex.ToString()));
}
finally
{
if (scene is not null && handler is not null)
{
try
{
TryUnloadScene(handler, scene, options.Connection.Timeout);
}
catch
{
}
}
if (engine is not null && handler is not null)
{
try
{
engine.Disconnect();
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
}
}
})
{
IsBackground = true,
Name = "KarismaSaveSceneImageProbe"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completion.Task;
}
static Task<SceneCatalogProbeResult> CatalogScenesAsync(SceneCatalogOptions options)
{
var completion = new TaskCompletionSource<SceneCatalogProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -1381,6 +1556,70 @@ internal sealed record CounterProbeOptions(
}
}
internal sealed record SaveSceneImageOptions(
ProbeOptions Connection,
string ScenePath,
string SceneAlias,
string OutputPath,
int Width,
int Height,
int Frame)
{
public static SaveSceneImageOptions Parse(string[] args)
{
var connection = ProbeOptions.Parse(args);
string? scenePath = null;
string? sceneAlias = null;
string? outputPath = null;
var width = 320;
var height = 180;
var frame = -1;
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 "--output" when index + 1 < args.Length:
outputPath = args[++index];
break;
case "--width" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedWidth):
width = parsedWidth;
index++;
break;
case "--height" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedHeight):
height = parsedHeight;
index++;
break;
case "--frame" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedFrame):
frame = parsedFrame;
index++;
break;
}
}
if (string.IsNullOrWhiteSpace(scenePath))
{
throw new ArgumentException("--scene is required.");
}
if (string.IsNullOrWhiteSpace(outputPath))
{
throw new ArgumentException("--output is required.");
}
scenePath = Path.GetFullPath(scenePath);
outputPath = Path.GetFullPath(outputPath);
sceneAlias ??= Path.GetFileNameWithoutExtension(scenePath);
return new SaveSceneImageOptions(connection, scenePath, sceneAlias, outputPath, width, height, frame);
}
}
internal sealed record SceneCatalogOptions(
ProbeOptions Connection,
string RootPath,
@@ -1531,6 +1770,7 @@ internal sealed record FolderInspectionOptions(ProbeOptions Connection, string R
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 SaveSceneImageProbeResult(bool ConnectRequestAccepted, string SceneLoadOutcome, string SaveOutcome, string OutputPath, string Detail);
internal sealed record SceneCatalogProbeResult(
bool ConnectRequestAccepted,
int SceneCount,
@@ -1597,6 +1837,7 @@ internal sealed class ProbeEventHandler : KAEventHandler
private TaskCompletionSource<eKResult> _styleColorTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _visibleTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _setValueTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<eKResult> _saveSceneImageTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private TaskCompletionSource<ObjectInfosProbeResult> _objectInfosTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private IKAScene? _sceneToQueryOnLoad;
@@ -1616,6 +1857,8 @@ internal sealed class ProbeEventHandler : KAEventHandler
public Task<eKResult> SetValueTask => _setValueTask.Task;
public Task<eKResult> SaveSceneImageTask => _saveSceneImageTask.Task;
public Task<ObjectInfosProbeResult> ObjectInfosTask => _objectInfosTask.Task;
public void ResetLoadSceneTask() => _loadSceneTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -1630,6 +1873,8 @@ internal sealed class ProbeEventHandler : KAEventHandler
public void ResetSetValueTask() => _setValueTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ResetSaveSceneImageTask() => _saveSceneImageTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ResetObjectInfosTask() => _objectInfosTask = new TaskCompletionSource<ObjectInfosProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
public void ConfigureQueryObjectInfosOnLoad(IKAScene scene) => _sceneToQueryOnLoad = scene;
@@ -1717,7 +1962,11 @@ internal sealed class ProbeEventHandler : KAEventHandler
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 OnSaveSceneImage(eKResult Result, string SceneName)
{
Console.WriteLine($"[SDK] OnSaveSceneImage result={Result} scene={SceneName}");
_saveSceneImageTask.TrySetResult(Result);
}
public void OnSaveScene(eKResult Result, string SceneName) { }
public void OnUnloadScene(eKResult Result, string SceneName)
{