5.14 시작전

This commit is contained in:
2026-05-14 09:38:45 +09:00
parent 8b5c92194f
commit e76c37ef56
24 changed files with 3638 additions and 717 deletions

View File

@@ -274,6 +274,7 @@ internal static class CutFileAudit
payload.CounterNumberKeys,
Array.Empty<KarismaChartCellUpdate>(),
Array.Empty<KarismaPositionUpdate>(),
Array.Empty<KarismaCropKeyUpdate>(),
payload.StyleColorUpdates,
payload.VisibilityUpdates,
CancellationToken.None)
@@ -524,6 +525,10 @@ internal static class CutFileAudit
string scenario,
string frameLabel)
{
_ = ShowWindow(pgmWindow.Handle, ShowWindowRestore);
_ = SetForegroundWindow(pgmWindow.Handle);
Thread.Sleep(80);
var fileName = $"{result.Index:000}_{SanitizeFileName(result.FolderName)}_{SanitizeFileName(result.BaseName)}_{scenario.ToLowerInvariant()}_{frameLabel}.png";
var outputPath = Path.Combine(options.CapturePath, fileName);
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
@@ -961,6 +966,12 @@ internal static class CutFileAudit
{
var normalizedBaseName = NormalizeVariantName(result.BaseName);
var explicitBaseName = TryResolveExplicitRgbSpec(result.FolderName, normalizedBaseName);
if (explicitBaseName is not null && string.IsNullOrWhiteSpace(explicitBaseName))
{
mappingKind = "explicit-none";
return null;
}
if (!string.IsNullOrWhiteSpace(explicitBaseName) &&
rgbCatalog.TryGetValue(BuildRgbCatalogKey(result.FolderName, explicitBaseName), out var explicitSpec))
{
@@ -1027,11 +1038,12 @@ internal static class CutFileAudit
Add("Elect2026_Normal_민방", "모든후보_교육감", "모든후보_교육감");
Add("Elect2026_Normal_민방", "사전_역대당선", "사전_역대당선자", "사전_역대당선자_기초단체장");
Add("Elect2026_Normal_민방", "사전_역대당선_교육감", "사전_역대당선자_교육감");
Add("Elect2026_Normal_민방", "이시각1위_광역단체장", "이시각1위_광역단체장");
Add("Elect2026_Normal_민방", "이시각1위_광역단체장_5760", "이시각1위_광역단체장_5760");
Add("Elect2026_Normal_민방", "이시각1위_기초단체장(5760동일)", "이시각1위_기초단체장");
Add("Elect2026_Normal_민방", string.Empty, "사전_역대투표율");
Add("Elect2026_Normal_민방", "이시각1위_광역단체장", "이시각1위_광역단체장", "이시각1위_광역단체장_HD");
Add("Elect2026_Normal_민방", "이시각1위_광역단체장_5760", "이시각1위_광역단체장_5760", "이시각1위_광역단체장_L");
Add("Elect2026_Normal_민방", "이시각1위_기초단체장(5760동일)", "이시각1위_기초단체장", "이시각1위_기초단체장_HD", "이시각1위_기초단체장_L");
Add("Elect2026_Normal_민방", "접전,초접전", "접전_광역단체장", "접전_기초단체장", "초접전_광역단체장", "초접전_기초단체장");
Add("Elect2026_Normal_민방", "판세_광역단체장", "판세_광역단체장", "판세_기초단체장");
Add("Elect2026_Normal_민방", "판세_광역단체장", "판세_광역단체장", "판세_기초단체장", "역대시도판세_광역단체장", "역대시도판세_기초단체장");
Add("Elect2026_Bottom_민방", "1-2위, 1-3위, 이시각1위", "1-2위_광역단체장", "1-2위_기초단체장", "1-3위_광역단체장", "1-3위_기초단체장", "1위_광역단체장", "1위_기초단체장");
Add("Elect2026_Bottom_민방", "당선", "당선_광역단체장", "당선_광역의원", "당선_기초단체장", "당선_기초의원");
Add("Elect2026_Bottom_민방", "모든후보", "전후보_광역단체장", "전후보_기초단체장");
@@ -1296,29 +1308,62 @@ internal static class CutFileAudit
private static PgmWindow? TryFindPgmWindow()
{
var process = Process.GetProcessesByName("Tornado3")
.FirstOrDefault(candidate => string.Equals(candidate.MainWindowTitle, "PGM", StringComparison.Ordinal));
if (process is null || process.MainWindowHandle == IntPtr.Zero)
var handle = IntPtr.Zero;
var tornadoProcessIds = Process.GetProcessesByName("Tornado3")
.Select(process => process.Id)
.ToHashSet();
EnumWindows((candidateHandle, lParam) =>
{
if (handle != IntPtr.Zero)
{
return false;
}
_ = GetWindowThreadProcessId(candidateHandle, out var processId);
if (!tornadoProcessIds.Contains((int)processId))
{
return true;
}
var titleLength = GetWindowTextLength(candidateHandle);
if (titleLength <= 0)
{
return true;
}
var title = new StringBuilder(titleLength + 1);
_ = GetWindowText(candidateHandle, title, title.Capacity);
if (string.Equals(title.ToString(), "PGM", StringComparison.Ordinal))
{
handle = candidateHandle;
return false;
}
return true;
}, IntPtr.Zero);
if (handle == IntPtr.Zero)
{
return null;
}
if (TryGetDwmExtendedFrameBounds(process.MainWindowHandle, out var dwmBounds))
if (TryGetDwmExtendedFrameBounds(handle, out var dwmBounds))
{
return new PgmWindow(process.MainWindowHandle, dwmBounds);
return new PgmWindow(handle, dwmBounds);
}
if (TryGetClientBounds(process.MainWindowHandle, out var clientBounds))
if (TryGetClientBounds(handle, out var clientBounds))
{
return new PgmWindow(process.MainWindowHandle, clientBounds);
return new PgmWindow(handle, clientBounds);
}
if (!GetWindowRect(process.MainWindowHandle, out var rect))
if (!GetWindowRect(handle, out var rect))
{
return null;
}
return new PgmWindow(process.MainWindowHandle, new Rect(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top));
return new PgmWindow(handle, new Rect(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top));
}
private static bool TryGetDwmExtendedFrameBounds(IntPtr handle, out Rect bounds)
@@ -1544,6 +1589,27 @@ internal static class CutFileAudit
return value.Replace("|", "\\|", StringComparison.Ordinal);
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, out NativeRect lpRect);
@@ -1560,6 +1626,8 @@ internal static class CutFileAudit
private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out NativeRect pvAttribute, int cbAttribute);
private const int DwmwaExtendedFrameBounds = 9;
private const int ShowWindowRestore = 9;
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
private readonly record struct AuditScene(string ScenePath, string RelativePath, string FolderName, string BaseName, BroadcastChannel Channel);
private readonly record struct AuditChannelBinding(int OutputChannelIndex, int LayerNo);