Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DGLabPunish v0.6.1
BepInEx\plugins\DGLabPunish\DGLabPunish.dll
Decompiled 20 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Net.Codecrete.QrCodeGenerator; using Newtonsoft.Json; using UnityEngine; using WebSocketSharp; using WebSocketSharp.Server; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace DGLabPunish; internal enum ContinuousStimState { Idle, Walk, Run, Slide, Punish } internal sealed class StimOverlay { public char Channel; public float StartTime; public int DurationMs; public int Strength; public int Frequency; public StimEnvelopeShape Shape; public double Phase; public int Priority; public int Generation; } internal sealed class ContinuousStimEngine { private struct Sample { public int Strength; public int Frequency; public int BaseStrength; public int OverlayStrength; } private readonly DGLabSocketServer _server; private readonly StimDiagnostics _diagnostics; private readonly List<StimOverlay> _overlays; private float _lastSendTime; private float _lastActiveTime; private float _lastQueueUpdateTime; private float _lastStrengthTimeA; private float _lastStrengthTimeB; private bool _moving; private bool _sprinting; private bool _sliding; private float _speed; private bool _forceSend; private int _generation; private int _estimatedQueueMs; private int _lastStrengthA; private int _lastStrengthB; internal ContinuousStimEngine(DGLabSocketServer server, StimDiagnostics diagnostics) { _server = server; _diagnostics = diagnostics; _overlays = new List<StimOverlay>(); _lastSendTime = -999f; _lastActiveTime = -999f; _lastQueueUpdateTime = -999f; _lastStrengthTimeA = -999f; _lastStrengthTimeB = -999f; _forceSend = false; _generation = 0; _estimatedQueueMs = 0; _lastStrengthA = -1; _lastStrengthB = -1; } internal void SetMovement(bool moving, bool sprinting, bool sliding, float speed) { if (moving != _moving || sprinting != _sprinting || sliding != _sliding) { _forceSend = true; } _moving = moving; _sprinting = sprinting; _sliding = sliding; _speed = speed; if (moving || sliding) { _lastActiveTime = Time.realtimeSinceStartup; } } internal void AddOverlay(char channel, int strength, int durationMs, int frequency, StimEnvelopeShape shape, double phase, int priority) { if (priority >= 80) { Interrupt(priority); } AddOverlayInternal(channel, strength, durationMs, frequency, shape, phase, priority); } private void AddOverlayInternal(char channel, int strength, int durationMs, int frequency, StimEnvelopeShape shape, double phase, int priority) { _overlays.Add(new StimOverlay { Channel = channel, StartTime = Time.realtimeSinceStartup, DurationMs = Mathf.Max(25, durationMs), Strength = Mathf.Max(0, strength), Frequency = Mathf.Clamp(frequency, 10, 1000), Shape = shape, Phase = phase, Priority = priority, Generation = _generation }); _forceSend = true; _lastSendTime = -999f; } internal void AddOverlayBoth(int strengthA, int strengthB, int durationMs, int frequency, StimEnvelopeShape shape, int priority) { AddOverlayPair(strengthA, strengthB, durationMs, frequency, shape, 0.0, 0.18, priority); } internal void AddOverlayPair(int strengthA, int strengthB, int durationMs, int frequency, StimEnvelopeShape shape, double phaseA, double phaseB, int priority) { if (priority >= 80) { Interrupt(priority); } AddOverlayInternal('A', strengthA, durationMs, frequency, shape, phaseA, priority); AddOverlayInternal('B', strengthB, durationMs, frequency, shape, phaseB, priority); } internal void Clear() { _overlays.Clear(); _lastSendTime = -999f; _lastActiveTime = -999f; _lastQueueUpdateTime = -999f; _forceSend = false; _generation++; _estimatedQueueMs = 0; _diagnostics.EstimatedQueueMs = 0; _diagnostics.ContinuousGeneration = _generation; } internal void Tick() { if (_server == null || !_server.IsBound || !ModConfig.Armed.Value || !ModConfig.ContinuousMode.Value) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; UpdateEstimatedQueue(realtimeSinceStartup); PruneOverlays(realtimeSinceStartup); int num = Mathf.Clamp(ModConfig.ContinuousSendIntervalMs.Value, 50, 1000); if (!_forceSend && (realtimeSinceStartup - _lastSendTime) * 1000f < (float)num) { return; } ContinuousStimState continuousStimState = CurrentState(realtimeSinceStartup); _diagnostics.ContinuousState = continuousStimState.ToString(); _diagnostics.ActiveOverlays = _overlays.Count; _diagnostics.EstimatedQueueMs = _estimatedQueueMs; _diagnostics.ContinuousGeneration = _generation; if (continuousStimState == ContinuousStimState.Idle && _overlays.Count == 0) { _diagnostics.ContinuousBaseA = 0; _diagnostics.ContinuousBaseB = 0; _diagnostics.ContinuousOverlayA = 0; _diagnostics.ContinuousOverlayB = 0; return; } int num2 = Mathf.Clamp(ModConfig.ContinuousLookaheadMs.Value, 100, 1500); int num3 = Mathf.Clamp(num2 / 2, 25, 80); if (!_forceSend && _estimatedQueueMs > num3) { _diagnostics.LastCommandResult = "队列水位足够,暂不补包"; return; } List<int> list = new List<int>(); List<int> list2 = new List<int>(); List<int> list3 = new List<int>(); List<int> list4 = new List<int>(); int num4 = Mathf.Max(1, (int)Math.Ceiling((double)num2 / 25.0)); int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; int num9 = 0; int num10 = 0; for (int i = 0; i < num4; i++) { float sampleTime = realtimeSinceStartup + (float)i * 0.025f; Sample sample = SampleChannel('A', continuousStimState, sampleTime); Sample sample2 = SampleChannel('B', continuousStimState, sampleTime); list.Add(sample.Frequency); list2.Add(sample2.Frequency); list3.Add(sample.Strength); list4.Add(sample2.Strength); num5 = Mathf.Max(num5, sample.Strength); num6 = Mathf.Max(num6, sample2.Strength); num7 = Mathf.Max(num7, sample.BaseStrength); num8 = Mathf.Max(num8, sample2.BaseStrength); num9 = Mathf.Max(num9, sample.OverlayStrength); num10 = Mathf.Max(num10, sample2.OverlayStrength); } PrepareStrength('A', num5); PrepareStrength('B', num6); List<string> list5 = WaveformEncoder.Pulse(list, list3); List<string> list6 = WaveformEncoder.Pulse(list2, list4); bool flag = _server.SendPulse('A', list5); bool flag2 = _server.SendPulse('B', list6); _lastSendTime = realtimeSinceStartup; _forceSend = false; if (flag || flag2) { int num11 = Mathf.Max(flag ? list5.Count : 0, flag2 ? list6.Count : 0); _estimatedQueueMs = Mathf.Clamp(_estimatedQueueMs + num11 * 100, 0, 1000); } _diagnostics.ContinuousBaseA = num7; _diagnostics.ContinuousBaseB = num8; _diagnostics.ContinuousOverlayA = num9; _diagnostics.ContinuousOverlayB = num10; _diagnostics.ContinuousRefillCount++; _diagnostics.EstimatedQueueMs = _estimatedQueueMs; _diagnostics.ContinuousGeneration = _generation; _diagnostics.LastCommandResult = ((flag && flag2) ? ("连续补包已发送,队列约 " + _estimatedQueueMs + "ms") : "连续补包发送失败"); } private ContinuousStimState CurrentState(float now) { if (_sliding) { return ContinuousStimState.Slide; } if (_sprinting && _moving) { return ContinuousStimState.Run; } if (_moving) { return ContinuousStimState.Walk; } int num = Mathf.Max(0, ModConfig.ContinuousFadeOutMs.Value); if (num > 0 && (now - _lastActiveTime) * 1000f < (float)num) { return ContinuousStimState.Walk; } if (_overlays.Count <= 0) { return ContinuousStimState.Idle; } return ContinuousStimState.Punish; } private Sample SampleChannel(char channel, ContinuousStimState state, float sampleTime) { int frequency = 180; int num = BaseStrength(state, sampleTime, out frequency); int num2 = 0; int num3 = frequency; for (int i = 0; i < _overlays.Count; i++) { StimOverlay stimOverlay = _overlays[i]; if (stimOverlay.Channel != channel || stimOverlay.Generation != _generation) { continue; } double num4 = (double)(sampleTime - stimOverlay.StartTime) * 1000.0 / (double)stimOverlay.DurationMs; if (!(num4 < 0.0) && !(num4 > 1.0)) { int num5 = (int)Math.Round((double)stimOverlay.Strength * WaveformEncoder.EvaluateEnvelope(stimOverlay.Shape, num4, stimOverlay.Phase)); if (num5 > num2) { num2 = num5; num3 = stimOverlay.Frequency; } } } int strength = Mathf.Clamp(num + num2, 0, ModConfig.MaxWaveIntensity.Value); if (num2 > 0) { frequency = num3; } Sample result = default(Sample); result.Strength = strength; result.Frequency = Mathf.Clamp(frequency, 10, 1000); result.BaseStrength = num; result.OverlayStrength = num2; return result; } private int BaseStrength(ContinuousStimState state, float sampleTime, out int frequency) { frequency = 180; int num; StimEnvelopeShape shape; switch (state) { case ContinuousStimState.Idle: case ContinuousStimState.Punish: return 0; case ContinuousStimState.Run: num = ModConfig.RunBaseIntensity.Value; frequency = ModConfig.RunBaseFrequency.Value; shape = ModConfig.RunBaseShape.Value; break; case ContinuousStimState.Slide: num = ModConfig.SlideBaseIntensity.Value; frequency = ModConfig.SlideBaseFrequency.Value; shape = StimEnvelopeShape.Tremor; break; default: num = ModConfig.WalkBaseIntensity.Value; frequency = ModConfig.WalkBaseFrequency.Value; shape = ModConfig.WalkBaseShape.Value; break; } int num2 = Mathf.Max(1, ModConfig.ContinuousFadeOutMs.Value); if (!_moving && !_sliding) { float num3 = (sampleTime - _lastActiveTime) * 1000f; if (num3 >= (float)num2) { return 0; } num = (int)Math.Round((double)num * (1.0 - (double)(num3 / (float)num2))); } double t = (double)sampleTime * 1000.0 % 900.0 / 900.0; double num4 = WaveformEncoder.EvaluateEnvelope(shape, t, 0.0); double num5 = 0.55; return Mathf.Clamp((int)Math.Round((double)num * (num5 + (1.0 - num5) * num4)), 0, ModConfig.MaxWaveIntensity.Value); } private void PruneOverlays(float now) { for (int num = _overlays.Count - 1; num >= 0; num--) { StimOverlay stimOverlay = _overlays[num]; if (stimOverlay.Generation != _generation || (now - stimOverlay.StartTime) * 1000f > (float)(stimOverlay.DurationMs + 100)) { _overlays.RemoveAt(num); } } } private void Interrupt(int priority) { _generation++; for (int num = _overlays.Count - 1; num >= 0; num--) { if (_overlays[num].Priority <= priority) { _overlays.RemoveAt(num); } } _server.ClearAll(); _estimatedQueueMs = 0; _lastQueueUpdateTime = Time.realtimeSinceStartup; _lastSendTime = -999f; _forceSend = true; _diagnostics.EstimatedQueueMs = 0; _diagnostics.ContinuousGeneration = _generation; } private void UpdateEstimatedQueue(float now) { if (_lastQueueUpdateTime < -100f) { _lastQueueUpdateTime = now; return; } int num = Mathf.Max(0, (int)Math.Round((now - _lastQueueUpdateTime) * 1000f)); if (num > 0) { _estimatedQueueMs = Mathf.Max(0, _estimatedQueueMs - num); _lastQueueUpdateTime = now; } } private void PrepareStrength(char channel, int eventStrength) { AutoStrengthMode autoStrengthMode = ModConfig.AutoStrengthMode.Value; if (ModConfig.AllowStrengthControl.Value) { autoStrengthMode = AutoStrengthMode.EventScaled; } if (autoStrengthMode == AutoStrengthMode.Off || eventStrength <= 0) { return; } StrengthState strength = _server.Strength; int channel2 = ((channel == 'A') ? 1 : 2); int num = ((channel == 'A') ? strength.ACurrent : strength.BCurrent); int num2 = ((channel == 'A') ? strength.AMax : strength.BMax); int num3 = Mathf.Clamp(ModConfig.MinimumChannelStrength.Value, 0, 200); int num4 = ((autoStrengthMode == AutoStrengthMode.MinimumOnly) ? Mathf.Max(num, num3) : Mathf.Max(num3, eventStrength)); if (num2 > 0) { num4 = Mathf.Min(num4, num2); } num4 = Mathf.Clamp(num4, 0, 200); float realtimeSinceStartup = Time.realtimeSinceStartup; int num5 = ((channel == 'A') ? _lastStrengthA : _lastStrengthB); float num6 = ((channel == 'A') ? _lastStrengthTimeA : _lastStrengthTimeB); if (num5 < 0 || Mathf.Abs(num4 - num5) >= 2 || !(realtimeSinceStartup - num6 < 0.35f)) { _server.SetStrength(channel2, num4); if (channel == 'A') { _lastStrengthA = num4; _lastStrengthTimeA = realtimeSinceStartup; } else { _lastStrengthB = num4; _lastStrengthTimeB = realtimeSinceStartup; } } } } internal sealed class ControlPanel { private readonly DGLabSocketServer _server; private readonly StimController _stim; private Rect _window; private Vector2 _scroll; private Texture2D _qrTexture; private string _qrText; private bool _visible; private bool _cursorSaved; private bool _previousCursorVisible; private CursorLockMode _previousLockState; private bool _draftsInitialized; private string _hostDraft; private string _portDraft; private string _publishUriDraft; private string _remoteUriDraft; private string _remoteCodeDraft; private string _panelKeyDraft; private string _stopKeyDraft; private string _settingsMessage; private int _panelTab; private int _selectedProfileIndex; private int _selectedWaveIndex; private string _manualStrengthA; private string _manualStrengthB; internal bool Visible => _visible; internal ControlPanel(DGLabSocketServer server, StimController stim) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) _server = server; _stim = stim; _window = new Rect(40f, 40f, 560f, 760f); _scroll = Vector2.zero; _visible = false; _settingsMessage = ""; _panelTab = 0; _selectedProfileIndex = 0; _selectedWaveIndex = 0; _manualStrengthA = "20"; _manualStrengthB = "20"; } internal void ToggleVisible() { SetVisible(!_visible); } internal void SetVisible(bool visible) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (_visible != visible) { _visible = visible; if (_visible) { InitDrafts(); _previousCursorVisible = Cursor.visible; _previousLockState = Cursor.lockState; _cursorSaved = true; UnlockCursor(); } else if (_cursorSaved) { Cursor.visible = _previousCursorVisible; Cursor.lockState = _previousLockState; _cursorSaved = false; } } } internal void UpdateCursor() { if (_visible) { UnlockCursor(); } } private static void UnlockCursor() { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } internal void Draw() { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if (_visible) { float num = Mathf.Max(420f, (float)Screen.width - 20f); float num2 = Mathf.Max(360f, (float)Screen.height * 0.85f); ((Rect)(ref _window)).width = Mathf.Min(((Rect)(ref _window)).width, num); ((Rect)(ref _window)).height = Mathf.Min(((Rect)(ref _window)).height, num2); if (((Rect)(ref _window)).xMax > (float)Screen.width) { ((Rect)(ref _window)).x = Mathf.Max(0f, (float)Screen.width - ((Rect)(ref _window)).width - 10f); } if (((Rect)(ref _window)).yMax > (float)Screen.height) { ((Rect)(ref _window)).y = Mathf.Max(0f, (float)Screen.height - ((Rect)(ref _window)).height - 10f); } _window = GUI.Window(88991, _window, new WindowFunction(DrawWindow), "郊狼 3.0 连接面板"); } } private void DrawWindow(int id) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) InitDrafts(); _scroll = GUILayout.BeginScrollView(_scroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Max(260f, ((Rect)(ref _window)).height - 34f)) }); DrawTabs(); if (_panelTab == 0) { DrawClientPage(); FinishWindow(); return; } if (_panelTab == 1) { DrawWaveLibraryPage(); FinishWindow(); return; } if (_panelTab == 2) { DrawEventMappingPage(); FinishWindow(); return; } if (_panelTab == 3) { DrawImportExportPage(); FinishWindow(); return; } Section("连接"); GUILayout.Label("协议:DG-LAB 郊狼 3.0 App Socket", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("模式:" + _server.ConnectionModeText(), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("状态:" + _server.StatusText(), (GUILayoutOption[])(object)new GUILayoutOption[0]); if (!string.IsNullOrEmpty(_server.LastRemoteEndpoint)) { GUILayout.Label("连接来源/远程地址:" + _server.LastRemoteEndpoint, (GUILayoutOption[])(object)new GUILayoutOption[0]); } EnumCycle<SocketConnectionMode>("连接模式", ModConfig.ConnectionMode); GUILayout.Label("二维码地址(用于核对):", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.TextArea(_server.GetQrUrl(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(48f) }); EnsureQrTexture(_server.GetQrUrl()); if ((Object)(object)_qrTexture != (Object)null) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.FlexibleSpace(); GUILayout.Box((Texture)(object)_qrTexture, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(180f), GUILayout.Height(180f) }); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Label("检测到的电脑 IPv4:" + _server.LocalAddressSummary(), (GUILayoutOption[])(object)new GUILayoutOption[0]); string[] localIPv4Addresses = _server.GetLocalIPv4Addresses(); if (localIPv4Addresses.Length > 0) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); for (int i = 0; i < localIPv4Addresses.Length && i < 3; i++) { if (GUILayout.Button("使用 " + localIPv4Addresses[i], (GUILayoutOption[])(object)new GUILayoutOption[0])) { _hostDraft = localIPv4Addresses[i]; } } GUILayout.EndHorizontal(); } TextRow("局域网 IP/域名", ref _hostDraft); TextRow("本地端口", ref _portDraft); TextRow("公网 Socket URI", ref _publishUriDraft); TextRow("远程 Socket URI", ref _remoteUriDraft); TextRow("自建 Relay Code", ref _remoteCodeDraft); GUILayout.Label("说明:Relay Code 不是 DG-LAB App 原生远程口令,需要你自己的兼容 relay 服务。优先用公网/远程 Socket。", (GUILayoutOption[])(object)new GUILayoutOption[0]); TextRow("面板键", ref _panelKeyDraft); TextRow("急停键", ref _stopKeyDraft); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("保存连接/键位并重启", (GUILayoutOption[])(object)new GUILayoutOption[0])) { SavePanelSettings(restartServer: true); } if (GUILayout.Button(_server.IsRunning ? "重启服务" : "启动服务", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.Stop(); _server.Start(); RefreshQr(); } GUILayout.EndHorizontal(); if (GUILayout.Button("停止服务", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.Stop(); } if (!string.IsNullOrEmpty(_settingsMessage)) { GUILayout.Label(_settingsMessage, (GUILayoutOption[])(object)new GUILayoutOption[0]); } Section("状态与测试"); GUILayout.Label("触发总开关:" + (ModConfig.Armed.Value ? "已启用" : "未启用") + " 绑定后自动启用:" + (ModConfig.AutoArmOnBind.Value ? "开启" : "关闭"), (GUILayoutOption[])(object)new GUILayoutOption[0]); if (!ModConfig.Armed.Value) { GUILayout.Label("注意:真实游戏事件需要启用触发;下方模拟按钮会绕过此开关,只用于测试波形。", (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button(ModConfig.Armed.Value ? "解除触发" : "启用触发", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ModConfig.Armed.Value = !ModConfig.Armed.Value; SaveConfig(); } if (GUILayout.Button("紧急停止", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.EmergencyStop(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("测试 A", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SendTest('A'); } if (GUILayout.Button("测试 B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SendTest('B'); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟左脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.TriggerFootstepForced(left: true); } if (GUILayout.Button("模拟右脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.TriggerFootstepForced(left: false); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟奔跑左脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateRunFootstep(left: true); } if (GUILayout.Button("模拟奔跑右脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateRunFootstep(left: false); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟持续走路 2秒", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateContinuousMovement(run: false); } if (GUILayout.Button("模拟持续奔跑 2秒", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateContinuousMovement(run: true); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟跳跃", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateJump(); } if (GUILayout.Button("模拟落地", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateLand(); } if (GUILayout.Button("模拟滑行", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateSlide(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟包络受击", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateHurt(10); } if (GUILayout.Button("模拟死亡", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateDeath(); } GUILayout.EndHorizontal(); StrengthState strength = _server.Strength; GUILayout.Label("A 强度:" + strength.ACurrent + " / " + strength.AMax + " B 强度:" + strength.BCurrent + " / " + strength.BMax, (GUILayoutOption[])(object)new GUILayoutOption[0]); if ((strength.ACurrent == 0 || strength.BCurrent == 0) && ModConfig.AutoStrengthMode.Value == AutoStrengthMode.Off && !ModConfig.AllowStrengthControl.Value) { GUILayout.Label("提示:有通道当前强度为 0,且自动调强关闭;该通道可能没有体感。", (GUILayoutOption[])(object)new GUILayoutOption[0]); } StimDiagnostics diagnostics = _stim.Diagnostics; GUILayout.Label("连续:" + diagnostics.ContinuousState + " 补包:" + diagnostics.ContinuousRefillCount + " 队列估算:" + diagnostics.EstimatedQueueMs + "ms Gen:" + diagnostics.ContinuousGeneration, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("Overlay:" + diagnostics.ActiveOverlays, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("连续强度:A 基础 " + diagnostics.ContinuousBaseA + " + " + diagnostics.ContinuousOverlayA + " / B 基础 " + diagnostics.ContinuousBaseB + " + " + diagnostics.ContinuousOverlayB, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("步态:" + diagnostics.MovementState + " 来源:" + diagnostics.LastFootstepSource + " 间隔:" + diagnostics.LastFootstepIntervalMs + "ms", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("计数:左脚 " + diagnostics.LeftFootsteps + " / 右脚 " + diagnostics.RightFootsteps + " / 动画脚步 " + diagnostics.AnimationFootsteps + " / 兜底 " + diagnostics.FallbackFootsteps, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("去重:" + diagnostics.DuplicateFootsteps + " / 奔跑补拍 " + diagnostics.RunSupplementFootsteps + " / 左右间隔 " + diagnostics.LastLeftRightDeltaMs + "ms", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("动作:跳跃 " + diagnostics.JumpCount + " / 落地 " + diagnostics.LandCount + " / 滑行 " + diagnostics.SlideCount + " / 敌脚 " + diagnostics.EnemyFootsteps, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("事件:受击 " + diagnostics.HurtCount + " / 死亡 " + diagnostics.DeathCount, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("最后事件:" + diagnostics.LastEvent + " 通道:" + diagnostics.LastChannel + " 结果:" + diagnostics.LastCommandResult, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("最后拦截:" + diagnostics.LastBlockedReason, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("下一步:" + (diagnostics.NextFootLeft ? "左脚" : "右脚"), (GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("重置下一步为配置的第一脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.ResetFootToConfiguredFirst(); } Section("事件开关"); Toggle("本地玩家受击触发", ModConfig.HurtEnabled); Toggle("本地玩家死亡触发", ModConfig.DeathEnabled); Toggle("本地玩家脚步触发", ModConfig.LocalFootstepEnabled); Toggle("跳跃触发", ModConfig.JumpEnabled); Toggle("落地触发", ModConfig.LandEnabled); Toggle("滑行触发", ModConfig.SlideEnabled); Toggle("跳跃/落地/滑行总开关", ModConfig.LandJumpEnabled); Toggle("猎人脚步近距离提示", ModConfig.EnemyFootstepEnabled); Section("安全与自动强度"); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("舒适持续", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyComfortPreset(); } if (GUILayout.Button("标准游戏", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyPreset(20, 24, 30, 45, 60, 30, 65); } if (GUILayout.Button("强惩罚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyPreset(28, 36, 42, 65, 85, 35, 90); } if (GUILayout.Button("调试同步", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ApplyPreset(36, 45, 55, 70, 90, 35, 90); } GUILayout.EndHorizontal(); Toggle("连续模式", ModConfig.ContinuousMode); IntSlider("补包间隔 ms", ModConfig.ContinuousSendIntervalMs, 50, 1000); IntSlider("Lookahead ms", ModConfig.ContinuousLookaheadMs, 100, 1500); IntSlider("停止淡出 ms", ModConfig.ContinuousFadeOutMs, 0, 1500); Toggle("绑定后自动启用触发", ModConfig.AutoArmOnBind); EnumCycle<AutoStrengthMode>("自动调强模式", ModConfig.AutoStrengthMode); Toggle("兼容旧自动调强开关", ModConfig.AllowStrengthControl); IntSlider("最低可感强度", ModConfig.MinimumChannelStrength, 0, 80); IntSlider("总强度上限", ModConfig.MaxWaveIntensity, 1, 100); IntSlider("测试强度", ModConfig.TestIntensity, 1, 100); IntSlider("最长持续 ms", ModConfig.MaxEventDurationMs, 100, 6000); IntSlider("Clear 延迟 ms", ModConfig.ClearDelayMs, 0, 1000); Section("脚步"); Toggle("第一步为左脚", ModConfig.FirstFootLeft); EnumCycle<StimChannel>("左脚通道", ModConfig.LeftFootChannel); EnumCycle<StimChannel>("右脚通道", ModConfig.RightFootChannel); Toggle("站停后重置左右脚", ModConfig.ResetFootOnIdle); IntSlider("站停重置 ms", ModConfig.FootstepIdleResetMs, 100, 3000); IntSlider("脚步强度", ModConfig.FootstepIntensity, 1, 50); IntSlider("脚步持续 ms", ModConfig.FootstepDurationMs, 25, 600); IntSlider("脚步频率", ModConfig.FootstepFrequency, 10, 1000); EnumCycle<StimWaveShape>("脚步波形", ModConfig.FootstepWaveShape); IntSlider("走路基础强度", ModConfig.WalkBaseIntensity, 0, 50); IntSlider("走路基础频率", ModConfig.WalkBaseFrequency, 10, 1000); EnumCycle<StimEnvelopeShape>("走路基础波形", ModConfig.WalkBaseShape); IntSlider("走路脚步增量", ModConfig.WalkStepBumpIntensity, 0, 60); IntSlider("走路增量持续 ms", ModConfig.WalkStepBumpDurationMs, 25, 600); IntSlider("动画去重 ms", ModConfig.FootstepDuplicateWindowMs, 0, 200); IntSlider("走路最小间隔 ms", ModConfig.WalkFootstepMinIntervalMs, 20, 500); IntSlider("奔跑基础强度", ModConfig.RunBaseIntensity, 0, 60); IntSlider("奔跑基础频率", ModConfig.RunBaseFrequency, 10, 1000); EnumCycle<StimEnvelopeShape>("奔跑基础波形", ModConfig.RunBaseShape); IntSlider("奔跑脚步强度", ModConfig.RunFootstepIntensity, 1, 80); IntSlider("奔跑持续 ms", ModConfig.RunFootstepDurationMs, 25, 600); IntSlider("奔跑脚步增量", ModConfig.RunStepBumpIntensity, 0, 80); IntSlider("奔跑增量持续 ms", ModConfig.RunStepBumpDurationMs, 25, 600); IntSlider("奔跑最小间隔 ms", ModConfig.RunFootstepMinIntervalMs, 20, 500); Toggle("兜底脚步补发", ModConfig.FallbackFootstepSupplement); IntSlider("兜底等待 ms", ModConfig.FallbackFootstepNoAnimationMs, 100, 2000); Toggle("奔跑节奏补拍", ModConfig.RunCadenceSupplement); IntSlider("奔跑补拍间隔 ms", ModConfig.RunSupplementIntervalMs, 55, 500); Section("动作"); EnumCycle<StimChannelMode>("跳跃通道", ModConfig.JumpChannelMode); IntSlider("跳跃强度", ModConfig.JumpIntensity, 1, 80); IntSlider("跳跃持续 ms", ModConfig.JumpDurationMs, 25, 1000); IntSlider("跳跃频率", ModConfig.JumpFrequency, 10, 1000); IntSlider("跳跃冷却 ms", ModConfig.JumpCooldownMs, 40, 2000); EnumCycle<StimChannelMode>("落地通道", ModConfig.LandChannelMode); IntSlider("落地强度", ModConfig.LandIntensity, 1, 80); IntSlider("落地持续 ms", ModConfig.LandDurationMs, 25, 1000); IntSlider("落地频率", ModConfig.LandFrequency, 10, 1000); EnumCycle<StimEnvelopeShape>("落地包络", ModConfig.LandEnvelope); IntSlider("落地冷却 ms", ModConfig.LandCooldownMs, 40, 2000); EnumCycle<StimChannelMode>("滑行通道", ModConfig.SlideChannelMode); IntSlider("滑行强度", ModConfig.SlideIntensity, 1, 80); IntSlider("滑行持续 ms", ModConfig.SlideDurationMs, 25, 1000); IntSlider("滑行频率", ModConfig.SlideFrequency, 10, 1000); EnumCycle<StimEnvelopeShape>("滑行包络", ModConfig.SlideEnvelope); IntSlider("滑行冷却 ms", ModConfig.SlideCooldownMs, 40, 2000); Section("受击"); EnumCycle<StimChannelMode>("受击通道", ModConfig.HurtChannelMode); IntSlider("受击基础强度", ModConfig.HurtMinIntensity, 1, 100); IntSlider("受击最大强度", ModConfig.HurtMaxIntensity, 1, 100); FloatSlider("伤害倍率", ModConfig.HurtDamageMultiplier, 0f, 10f); FloatSlider("A 通道倍率", ModConfig.HurtAMultiplier, 0f, 2f); FloatSlider("B 通道倍率", ModConfig.HurtBMultiplier, 0f, 2f); IntSlider("受击最短 ms", ModConfig.HurtDurationMinMs, 25, 6000); IntSlider("受击最长 ms", ModConfig.HurtDurationMaxMs, 25, 6000); IntSlider("受击频率", ModConfig.HurtFrequency, 10, 1000); EnumCycle<StimEnvelopeShape>("受击包络", ModConfig.HurtEnvelope); IntSlider("受击冷却 ms", ModConfig.HurtCooldownMs, 100, 3000); Section("死亡"); EnumCycle<StimChannelMode>("死亡通道", ModConfig.DeathChannelMode); EnumCycle<StimWaveShape>("死亡 A 波形", ModConfig.DeathAWaveShape); EnumCycle<StimWaveShape>("死亡 B 波形", ModConfig.DeathBWaveShape); EnumCycle<StimEnvelopeShape>("死亡包络", ModConfig.DeathEnvelope); Toggle("死亡前清空旧队列", ModConfig.DeathClearBeforePulse); IntSlider("死亡强度", ModConfig.DeathIntensity, 1, 100); IntSlider("死亡持续 ms", ModConfig.DeathDurationMs, 100, 6000); IntSlider("死亡频率", ModConfig.DeathFrequency, 10, 1000); IntSlider("死亡冷却 ms", ModConfig.DeathCooldownMs, 500, 10000); Section("说明"); GUILayout.Label("电脑不需要蓝牙。手机连接郊狼 3.0,Mod 只负责 Socket。", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("公网/远程模式请使用可被手机访问的 ws:// 或 wss:// 地址。正式远程建议 wss。", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("DG-LAB App 原生远程口令不是 Socket v2 API,本 Mod 不把它作为稳定主线接入。", (GUILayoutOption[])(object)new GUILayoutOption[0]); FinishWindow(); } private void FinishWindow() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) GUILayout.EndScrollView(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); } private void DrawTabs() { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Toggle(_panelTab == 0, "客户端", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[0])) { _panelTab = 0; } if (GUILayout.Toggle(_panelTab == 1, "波形库", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[0])) { _panelTab = 1; } if (GUILayout.Toggle(_panelTab == 2, "事件映射", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[0])) { _panelTab = 2; } if (GUILayout.Toggle(_panelTab == 3, "导入/导出", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[0])) { _panelTab = 3; } if (GUILayout.Toggle(_panelTab == 4, "事件参数", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[0])) { _panelTab = 4; } GUILayout.EndHorizontal(); } private void DrawClientPage() { Section("Socket 控制端"); GUILayout.Label("协议:DG-LAB 郊狼 3.0 App Socket v2。电脑不需要蓝牙,手机 App 负责蓝牙连接主机。", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("模式:" + _server.ConnectionModeText(), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("状态:" + _server.StatusText(), (GUILayoutOption[])(object)new GUILayoutOption[0]); if (!string.IsNullOrEmpty(_server.LastRemoteEndpoint)) { GUILayout.Label("连接来源/远程地址:" + _server.LastRemoteEndpoint, (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (!string.IsNullOrEmpty(_server.LastError)) { GUILayout.Label("最后错误:" + _server.LastError, (GUILayoutOption[])(object)new GUILayoutOption[0]); } GUILayout.Label("二维码地址:", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.TextArea(_server.GetQrUrl(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(48f) }); EnsureQrTexture(_server.GetQrUrl()); if ((Object)(object)_qrTexture != (Object)null) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.FlexibleSpace(); GUILayout.Box((Texture)(object)_qrTexture, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(180f), GUILayout.Height(180f) }); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Label("检测到的电脑 IPv4:" + _server.LocalAddressSummary(), (GUILayoutOption[])(object)new GUILayoutOption[0]); string[] localIPv4Addresses = _server.GetLocalIPv4Addresses(); if (localIPv4Addresses.Length > 0) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); for (int i = 0; i < localIPv4Addresses.Length && i < 3; i++) { if (GUILayout.Button("使用 " + localIPv4Addresses[i], (GUILayoutOption[])(object)new GUILayoutOption[0])) { _hostDraft = localIPv4Addresses[i]; } } GUILayout.EndHorizontal(); } EnumCycle<SocketConnectionMode>("连接模式", ModConfig.ConnectionMode); TextRow("局域网 IP/域名", ref _hostDraft); TextRow("本地端口", ref _portDraft); TextRow("公网 Socket URI", ref _publishUriDraft); TextRow("远程 Socket URI", ref _remoteUriDraft); TextRow("自建 Relay Code", ref _remoteCodeDraft); GUILayout.Label("Relay Code 是本 Mod 自建 relay 口令,不是 DG-LAB App 原生远程口令。", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("保存连接/键位并重启", (GUILayoutOption[])(object)new GUILayoutOption[0])) { SavePanelSettings(restartServer: true); } if (GUILayout.Button(_server.IsRunning ? "重启服务" : "启动服务", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.Stop(); _server.Start(); RefreshQr(); } if (GUILayout.Button("停止服务", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.Stop(); } GUILayout.EndHorizontal(); Section("强度控制"); StrengthState strength = _server.Strength; GUILayout.Label("A 当前/上限:" + strength.ACurrent + " / " + strength.AMax + " B 当前/上限:" + strength.BCurrent + " / " + strength.BMax, (GUILayoutOption[])(object)new GUILayoutOption[0]); TextRow("手动 A 强度", ref _manualStrengthA); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("设置 A", (GUILayoutOption[])(object)new GUILayoutOption[0])) { SetManualStrength(1, _manualStrengthA); } if (GUILayout.Button("A -5", (GUILayoutOption[])(object)new GUILayoutOption[0])) { AdjustStrength(1, -5); } if (GUILayout.Button("A +5", (GUILayoutOption[])(object)new GUILayoutOption[0])) { AdjustStrength(1, 5); } if (GUILayout.Button("Clear A", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.ClearChannel(1); } GUILayout.EndHorizontal(); TextRow("手动 B 强度", ref _manualStrengthB); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("设置 B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { SetManualStrength(2, _manualStrengthB); } if (GUILayout.Button("B -5", (GUILayoutOption[])(object)new GUILayoutOption[0])) { AdjustStrength(2, -5); } if (GUILayout.Button("B +5", (GUILayoutOption[])(object)new GUILayoutOption[0])) { AdjustStrength(2, 5); } if (GUILayout.Button("Clear B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.ClearChannel(2); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("启用触发", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ModConfig.Armed.Value = true; SaveConfig(); } if (GUILayout.Button("解除触发", (GUILayoutOption[])(object)new GUILayoutOption[0])) { ModConfig.Armed.Value = false; SaveConfig(); } if (GUILayout.Button("紧急停止", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.EmergencyStop(); } GUILayout.EndHorizontal(); Section("快速测试"); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("测试 A", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SendTest('A'); } if (GUILayout.Button("测试 B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SendTest('B'); } GUILayout.EndHorizontal(); WavePreset wavePreset = SelectedWave(); GUILayout.Label("选中波形:" + ((wavePreset == null) ? "无" : StimProfileManager.DisplayName(wavePreset)), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("播放选中波形 A", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.SendWave(_server, wavePreset, 'A'); } if (GUILayout.Button("播放选中波形 B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.SendWave(_server, wavePreset, 'B'); } if (GUILayout.Button("Clear A/B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _server.ClearAll(); } GUILayout.EndHorizontal(); if (!string.IsNullOrEmpty(_settingsMessage)) { GUILayout.Label(_settingsMessage, (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (!string.IsNullOrEmpty(StimProfileManager.LastMessage)) { GUILayout.Label(StimProfileManager.LastMessage, (GUILayoutOption[])(object)new GUILayoutOption[0]); } } private void DrawWaveLibraryPage() { Section("波形包"); DrawProfileSelector(); StimProfile stimProfile = SelectedProfile(); if (stimProfile == null) { GUILayout.Label("没有可用波形包。", (GUILayoutOption[])(object)new GUILayoutOption[0]); return; } GUILayout.Label("当前波形包:" + StimProfileManager.DisplayName(stimProfile) + " 版本:" + stimProfile.version + " 上限:" + stimProfile.maxWaveIntensity, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("应用此波形包", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.ApplyProfile(stimProfile); _settingsMessage = StimProfileManager.LastMessage; } if (GUILayout.Button("刷新波形包", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.RefreshProfiles(); ClampSelections(); } GUILayout.EndHorizontal(); Section("波形列表"); if (stimProfile.waves == null || stimProfile.waves.Count == 0) { GUILayout.Label("该波形包没有波形。", (GUILayoutOption[])(object)new GUILayoutOption[0]); return; } for (int i = 0; i < stimProfile.waves.Count; i++) { WavePreset wave = stimProfile.waves[i]; GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Toggle(_selectedWaveIndex == i, StimProfileManager.DisplayName(wave), GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) })) { _selectedWaveIndex = i; } GUILayout.Label(WaveSummary(wave), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.EndHorizontal(); } WavePreset wavePreset = SelectedWave(); if (wavePreset != null) { Section("波形测试"); GUILayout.Label("名称:" + StimProfileManager.DisplayName(wavePreset), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("类型:" + wavePreset.type + " / " + wavePreset.shape + " 通道建议:" + wavePreset.channel, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("频率:" + wavePreset.frequency + " 强度:" + wavePreset.minStrength + "-" + wavePreset.maxStrength + " 时长:" + wavePreset.durationMs + "ms", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("测试 A", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.SendWave(_server, wavePreset, 'A'); } if (GUILayout.Button("测试 B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.SendWave(_server, wavePreset, 'B'); } if (GUILayout.Button("测试 A+B", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.SendWave(_server, wavePreset, 'A'); StimProfileManager.SendWave(_server, wavePreset, 'B'); } GUILayout.EndHorizontal(); } if (!string.IsNullOrEmpty(StimProfileManager.LastMessage)) { GUILayout.Label(StimProfileManager.LastMessage, (GUILayoutOption[])(object)new GUILayoutOption[0]); } } private void DrawEventMappingPage() { Section("事件映射"); DrawProfileSelector(); StimProfile stimProfile = SelectedProfile(); if (stimProfile == null || stimProfile.events == null || stimProfile.events.Count == 0) { GUILayout.Label("当前波形包没有事件映射。", (GUILayoutOption[])(object)new GUILayoutOption[0]); return; } GUILayout.Label("这里展示波形包对事件的推荐映射;应用波形包会把持续状态参数写入配置。", (GUILayoutOption[])(object)new GUILayoutOption[0]); for (int i = 0; i < stimProfile.events.Count; i++) { EventRule eventRule = stimProfile.events[i]; GUILayout.Label(eventRule.eventName + " -> " + eventRule.waveName + " 通道:" + eventRule.channelMode + " 倍率:" + eventRule.strengthMultiplier.ToString("0.00"), (GUILayoutOption[])(object)new GUILayoutOption[0]); if (!string.IsNullOrEmpty(eventRule.note)) { GUILayout.Label(" " + eventRule.note, (GUILayoutOption[])(object)new GUILayoutOption[0]); } } Section("当前诊断"); StimDiagnostics diagnostics = _stim.Diagnostics; GUILayout.Label("连续状态:" + diagnostics.ContinuousState + " 补包:" + diagnostics.ContinuousRefillCount + " 队列估算:" + diagnostics.EstimatedQueueMs + "ms Gen:" + diagnostics.ContinuousGeneration, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("连续强度:A " + diagnostics.ContinuousBaseA + "+" + diagnostics.ContinuousOverlayA + " / B " + diagnostics.ContinuousBaseB + "+" + diagnostics.ContinuousOverlayB, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("脚步:左 " + diagnostics.LeftFootsteps + " / 右 " + diagnostics.RightFootsteps + " / 来源 " + diagnostics.LastFootstepSource + " / 下一步 " + (diagnostics.NextFootLeft ? "左" : "右"), (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("动作:跳跃 " + diagnostics.JumpCount + " / 落地 " + diagnostics.LandCount + " / 滑行 " + diagnostics.SlideCount + " / 受击 " + diagnostics.HurtCount + " / 死亡 " + diagnostics.DeathCount, (GUILayoutOption[])(object)new GUILayoutOption[0]); Section("模拟事件"); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟左脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.TriggerFootstepForced(left: true); } if (GUILayout.Button("模拟右脚", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.TriggerFootstepForced(left: false); } if (GUILayout.Button("模拟走路 2秒", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateContinuousMovement(run: false); } if (GUILayout.Button("模拟奔跑 2秒", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateContinuousMovement(run: true); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("模拟跳跃", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateJump(); } if (GUILayout.Button("模拟落地", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateLand(); } if (GUILayout.Button("模拟滑行", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateSlide(); } if (GUILayout.Button("模拟受击", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateHurt(10); } if (GUILayout.Button("模拟死亡", (GUILayoutOption[])(object)new GUILayoutOption[0])) { _stim.SimulateDeath(); } GUILayout.EndHorizontal(); } private void DrawImportExportPage() { Section("导入/导出"); GUILayout.Label("导入目录:", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.TextArea(StimProfileManager.ProfilesDirectory, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }); GUILayout.Label("支持 DGLabPunishProfile.json、原始 HEX JSON 数组和 .pulse 文本。第三方项目建议只导入数据,不复制代码。", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("HEX 示例:type=hex,hex=[\"0A0A0A0A64646464\"];.pulse 可每行写 16 位 HEX 或 frequency strength。", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); if (GUILayout.Button("刷新导入目录", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.RefreshProfiles(); ClampSelections(); } if (GUILayout.Button("导出当前配置", (GUILayoutOption[])(object)new GUILayoutOption[0])) { try { StimProfileManager.ExportCurrentConfig(); } catch (Exception ex) { StimProfileManager.LastMessage = "导出失败:" + ex.Message; } } GUILayout.EndHorizontal(); DrawProfileSelector(); if (GUILayout.Button("应用选中的导入/内置波形包", (GUILayoutOption[])(object)new GUILayoutOption[0])) { StimProfileManager.ApplyProfile(SelectedProfile()); _settingsMessage = StimProfileManager.LastMessage; } if (!string.IsNullOrEmpty(_settingsMessage)) { GUILayout.Label(_settingsMessage, (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (!string.IsNullOrEmpty(StimProfileManager.LastMessage)) { GUILayout.Label(StimProfileManager.LastMessage, (GUILayoutOption[])(object)new GUILayoutOption[0]); } Section("当前核心配置"); GUILayout.Label("连续模式:" + ModConfig.ContinuousMode.Value + " 自动调强:" + ModConfig.AutoStrengthMode.Value, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("补包/Lookahead:" + ModConfig.ContinuousSendIntervalMs.Value + " / " + ModConfig.ContinuousLookaheadMs.Value + " ms", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("走路基础/增量:" + ModConfig.WalkBaseIntensity.Value + " / " + ModConfig.WalkStepBumpIntensity.Value, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("奔跑基础/增量:" + ModConfig.RunBaseIntensity.Value + " / " + ModConfig.RunStepBumpIntensity.Value, (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("受击:" + ModConfig.HurtMinIntensity.Value + "-" + ModConfig.HurtMaxIntensity.Value + " / " + ModConfig.HurtDurationMaxMs.Value + "ms", (GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("死亡:" + ModConfig.DeathIntensity.Value + " / " + ModConfig.DeathDurationMs.Value + "ms", (GUILayoutOption[])(object)new GUILayoutOption[0]); } private void DrawProfileSelector() { ClampSelections(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label("波形包", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); if (GUILayout.Button("<", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(32f) })) { _selectedProfileIndex--; ClampSelections(); } StimProfile stimProfile = SelectedProfile(); GUILayout.Label((stimProfile == null) ? "无" : StimProfileManager.DisplayName(stimProfile), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); if (GUILayout.Button(">", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(32f) })) { _selectedProfileIndex++; ClampSelections(); } GUILayout.EndHorizontal(); } private StimProfile SelectedProfile() { ClampSelections(); if (StimProfileManager.Profiles.Count == 0) { return null; } return StimProfileManager.Profiles[_selectedProfileIndex]; } private WavePreset SelectedWave() { StimProfile stimProfile = SelectedProfile(); if (stimProfile == null || stimProfile.waves == null || stimProfile.waves.Count == 0) { return null; } ClampSelections(); return stimProfile.waves[_selectedWaveIndex]; } private void ClampSelections() { if (StimProfileManager.Profiles.Count == 0) { _selectedProfileIndex = 0; _selectedWaveIndex = 0; return; } if (_selectedProfileIndex < 0) { _selectedProfileIndex = StimProfileManager.Profiles.Count - 1; } if (_selectedProfileIndex >= StimProfileManager.Profiles.Count) { _selectedProfileIndex = 0; } StimProfile stimProfile = StimProfileManager.Profiles[_selectedProfileIndex]; int num = ((stimProfile != null && stimProfile.waves != null) ? stimProfile.waves.Count : 0); if (num == 0) { _selectedWaveIndex = 0; return; } if (_selectedWaveIndex < 0) { _selectedWaveIndex = num - 1; } if (_selectedWaveIndex >= num) { _selectedWaveIndex = 0; } } private string WaveSummary(WavePreset wave) { if (wave == null) { return ""; } if (wave.hex != null && wave.hex.Count > 0) { return "HEX " + wave.hex.Count + " 条 / 建议 " + wave.channel; } return wave.type + " " + wave.shape + " / " + wave.frequency + "Hz / " + wave.minStrength + "-" + wave.maxStrength + " / " + wave.durationMs + "ms"; } private void SetManualStrength(int channel, string value) { if (!int.TryParse(value, out var result)) { _settingsMessage = "强度必须是数字。"; return; } result = Mathf.Clamp(result, 0, 200); bool flag = _server.SetStrength(channel, result); _settingsMessage = (flag ? ("已设置 " + ((channel == 1) ? "A" : "B") + " 强度为 " + result) : "设置失败:App 尚未绑定或连接不可用。"); } private void AdjustStrength(int channel, int delta) { StrengthState strength = _server.Strength; int num = ((channel == 1) ? strength.ACurrent : strength.BCurrent); int num2 = Mathf.Clamp(num + delta, 0, 200); if (channel == 1) { _manualStrengthA = num2.ToString(); } else { _manualStrengthB = num2.ToString(); } bool flag = _server.SetStrength(channel, num2); _settingsMessage = (flag ? ("已调整 " + ((channel == 1) ? "A" : "B") + " 强度为 " + num2) : "调整失败:App 尚未绑定或连接不可用。"); } private void InitDrafts() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if (!_draftsInitialized) { _hostDraft = ModConfig.AdvertiseHost.Value; if (string.IsNullOrEmpty(_hostDraft)) { _hostDraft = _server.GetAdvertiseHost(); } _portDraft = ModConfig.Port.Value.ToString(); _publishUriDraft = ModConfig.PublishSocketUri.Value; _remoteUriDraft = ModConfig.RemoteServerUri.Value; _remoteCodeDraft = ModConfig.RemotePairCode.Value; _panelKeyDraft = ((object)ModConfig.PanelKey.Value).ToString(); _stopKeyDraft = ((object)ModConfig.EmergencyStopKey.Value).ToString(); _draftsInitialized = true; } } private void SavePanelSettings(bool restartServer) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if (!int.TryParse(_portDraft, out var result) || result < 1 || result > 65535) { _settingsMessage = "端口无效,应为 1-65535。"; return; } if (!TryParseKey(_panelKeyDraft, out var keyCode)) { _settingsMessage = "面板键无效,例如 P、F8、Insert。"; return; } if (!TryParseKey(_stopKeyDraft, out var keyCode2)) { _settingsMessage = "急停键无效,例如 I、F9、BackQuote。"; return; } ModConfig.AdvertiseHost.Value = (_hostDraft ?? "").Trim(); ModConfig.Port.Value = result; ModConfig.PublishSocketUri.Value = (_publishUriDraft ?? "").Trim(); ModConfig.RemoteServerUri.Value = (_remoteUriDraft ?? "").Trim(); ModConfig.RemotePairCode.Value = (_remoteCodeDraft ?? "").Trim(); ModConfig.PanelKey.Value = keyCode; ModConfig.EmergencyStopKey.Value = keyCode2; SaveConfig(); RefreshQr(); if (restartServer) { _server.Stop(); _server.Start(); } _settingsMessage = "设置已保存。当前二维码已刷新。"; } private static bool TryParseKey(string value, out KeyCode keyCode) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected I4, but got Unknown try { keyCode = (KeyCode)(int)(KeyCode)Enum.Parse(typeof(KeyCode), (value ?? "").Trim(), ignoreCase: true); return true; } catch { keyCode = (KeyCode)0; return false; } } private static void SaveConfig() { try { if ((Object)(object)Plugin.Instance != (Object)null) { ((BaseUnityPlugin)Plugin.Instance).Config.Save(); } } catch { } } private static void Section(string label) { GUILayout.Space(8f); GUILayout.Label("=== " + label + " ===", (GUILayoutOption[])(object)new GUILayoutOption[0]); } private void TextRow(string label, ref string value) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); value = GUILayout.TextField(value ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.EndHorizontal(); } private void Toggle(string label, ConfigEntry<bool> entry) { bool flag = GUILayout.Toggle(entry.Value, label, (GUILayoutOption[])(object)new GUILayoutOption[0]); if (flag != entry.Value) { entry.Value = flag; SaveConfig(); } } private void ApplyPreset(int footstep, int slide, int jump, int land, int death, int minimum, int hurtMax) { ModConfig.AutoStrengthMode.Value = AutoStrengthMode.EventScaled; ModConfig.ContinuousMode.Value = true; ModConfig.MinimumChannelStrength.Value = minimum; ModConfig.MaxWaveIntensity.Value = Mathf.Max(hurtMax, death); ModConfig.FootstepIntensity.Value = footstep; ModConfig.FootstepDurationMs.Value = ((footstep >= 24) ? 320 : 250); ModConfig.FootstepWaveShape.Value = StimWaveShape.Sin; ModConfig.WalkBaseIntensity.Value = Mathf.Max(4, footstep / 2); ModConfig.WalkBaseFrequency.Value = 25; ModConfig.WalkBaseShape.Value = StimEnvelopeShape.Tremor; ModConfig.WalkStepBumpIntensity.Value = Mathf.Max(8, footstep - 4); ModConfig.WalkStepBumpDurationMs.Value = 160; ModConfig.WalkFootstepMinIntervalMs.Value = 90; ModConfig.RunBaseIntensity.Value = Mathf.Max(6, footstep / 2 + 5); ModConfig.RunBaseFrequency.Value = 260; ModConfig.RunBaseShape.Value = StimEnvelopeShape.SawBurst; ModConfig.RunFootstepIntensity.Value = Mathf.Max(footstep + 4, footstep); ModConfig.RunFootstepDurationMs.Value = ((footstep >= 28) ? 170 : 140); ModConfig.RunStepBumpIntensity.Value = Mathf.Max(12, footstep); ModConfig.RunStepBumpDurationMs.Value = 110; ModConfig.RunFootstepMinIntervalMs.Value = 55; ModConfig.SlideIntensity.Value = slide; ModConfig.JumpIntensity.Value = jump; ModConfig.LandIntensity.Value = land; ModConfig.HurtMaxIntensity.Value = hurtMax; ModConfig.HurtDurationMinMs.Value = ((hurtMax >= 80) ? 1800 : 1500); ModConfig.HurtDurationMaxMs.Value = ((hurtMax >= 80) ? 4500 : 3500); ModConfig.HurtFrequency.Value = 60; ModConfig.DeathIntensity.Value = death; ModConfig.DeathDurationMs.Value = ((death >= 80) ? 5000 : 4000); ModConfig.DeathFrequency.Value = 150; ModConfig.MaxEventDurationMs.Value = Mathf.Max(ModConfig.DeathDurationMs.Value, ModConfig.HurtDurationMaxMs.Value); ModConfig.MaxQueuedPulseItems.Value = ((death >= 80) ? 55 : 45); ModConfig.TestIntensity.Value = minimum; ModConfig.HurtEnvelope.Value = StimEnvelopeShape.RampUpHoldDown; ModConfig.LandEnvelope.Value = StimEnvelopeShape.SingleTap; ModConfig.SlideEnvelope.Value = StimEnvelopeShape.Tremor; ModConfig.DeathEnvelope.Value = StimEnvelopeShape.DeathWave; SaveConfig(); _settingsMessage = "已应用预设,可继续微调各事件强度。"; } private void ApplyComfortPreset() { ModConfig.AutoStrengthMode.Value = AutoStrengthMode.EventScaled; ModConfig.ContinuousMode.Value = true; ModConfig.MinimumChannelStrength.Value = 10; ModConfig.MaxWaveIntensity.Value = 40; ModConfig.MaxEventDurationMs.Value = 3500; ModConfig.MaxQueuedPulseItems.Value = 35; ModConfig.ContinuousSendIntervalMs.Value = 80; ModConfig.ContinuousLookaheadMs.Value = 120; ModConfig.ContinuousFadeOutMs.Value = 450; ModConfig.FootstepIntensity.Value = 12; ModConfig.FootstepDurationMs.Value = 220; ModConfig.FootstepWaveShape.Value = StimWaveShape.Sin; ModConfig.WalkBaseIntensity.Value = 10; ModConfig.WalkBaseFrequency.Value = 25; ModConfig.WalkBaseShape.Value = StimEnvelopeShape.Tremor; ModConfig.WalkStepBumpIntensity.Value = 8; ModConfig.WalkStepBumpDurationMs.Value = 150; ModConfig.WalkFootstepMinIntervalMs.Value = 90; ModConfig.RunBaseIntensity.Value = 12; ModConfig.RunBaseFrequency.Value = 240; ModConfig.RunBaseShape.Value = StimEnvelopeShape.SawBurst; ModConfig.RunFootstepIntensity.Value = 14; ModConfig.RunFootstepDurationMs.Value = 130; ModConfig.RunStepBumpIntensity.Value = 10; ModConfig.RunStepBumpDurationMs.Value = 100; ModConfig.RunFootstepMinIntervalMs.Value = 55; ModConfig.SlideIntensity.Value = 14; ModConfig.JumpIntensity.Value = 16; ModConfig.LandIntensity.Value = 18; ModConfig.HurtMinIntensity.Value = 10; ModConfig.HurtMaxIntensity.Value = 40; ModConfig.HurtDurationMinMs.Value = 1200; ModConfig.HurtDurationMaxMs.Value = 3000; ModConfig.HurtFrequency.Value = 60; ModConfig.DeathIntensity.Value = 40; ModConfig.DeathDurationMs.Value = 3500; ModConfig.DeathFrequency.Value = 150; ModConfig.TestIntensity.Value = 10; ModConfig.HurtEnvelope.Value = StimEnvelopeShape.RampUpHoldDown; ModConfig.LandEnvelope.Value = StimEnvelopeShape.SingleTap; ModConfig.SlideEnvelope.Value = StimEnvelopeShape.Tremor; ModConfig.DeathEnvelope.Value = StimEnvelopeShape.DeathWave; SaveConfig(); _settingsMessage = "已应用舒适模式:最高 40,基础强度 10。"; } private void IntSlider(string label, ConfigEntry<int> entry, int min, int max) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(label + ":" + entry.Value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); int num = (int)Math.Round(GUILayout.HorizontalSlider((float)entry.Value, (float)min, (float)max, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) })); if (num != entry.Value) { entry.Value = num; SaveConfig(); } GUILayout.EndHorizontal(); } private void FloatSlider(string label, ConfigEntry<float> entry, float min, float max) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(label + ":" + entry.Value.ToString("0.00"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); float num = GUILayout.HorizontalSlider(entry.Value, min, max, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); num = (float)Math.Round(num, 2); if (Math.Abs(num - entry.Value) > 0.001f) { entry.Value = num; SaveConfig(); } GUILayout.EndHorizontal(); } private void EnumCycle<T>(string label, ConfigEntry<T> entry) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); if (GUILayout.Button(entry.Value.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { T[] array = (T[])Enum.GetValues(typeof(T)); int num = Array.IndexOf(array, entry.Value); if (num < 0) { num = 0; } entry.Value = array[(num + 1) % array.Length]; SaveConfig(); RefreshQr(); } GUILayout.EndHorizontal(); } private void RefreshQr() { _qrText = null; if ((Object)(object)_qrTexture != (Object)null) { Object.Destroy((Object)(object)_qrTexture); _qrTexture = null; } } private void EnsureQrTexture(string text) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_qrTexture != (Object)null && _qrText == text) { return; } RefreshQr(); _qrText = text; try { QrCode val = QrCode.EncodeText(text, Ecc.Medium); int num = 4; int num2 = Math.Max(3, 180 / (val.Size + num * 2)); int num3 = (val.Size + num * 2) * num2; Texture2D val2 = new Texture2D(num3, num3, (TextureFormat)4, false); Color32 val3 = default(Color32); ((Color32)(ref val3))..ctor((byte)0, (byte)0, (byte)0, byte.MaxValue); Color32 val4 = default(Color32); ((Color32)(ref val4))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); for (int i = 0; i < num3; i++) { for (int j = 0; j < num3; j++) { int num4 = j / num2 - num; int num5 = i / num2 - num; bool flag = num4 >= 0 && num5 >= 0 && num4 < val.Size && num5 < val.Size && val.GetModule(num4, num5); val2.SetPixel(j, num3 - 1 - i, Color32.op_Implicit(flag ? val3 : val4)); } } val2.Apply(false, true); _qrTexture = val2; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to generate QR texture: " + ex.Message)); } } } internal sealed class DgLabMessage { [JsonProperty("type")] public string Type { get; set; } [JsonProperty("clientId")] public string ClientId { get; set; } [JsonProperty("targetId")] public string TargetId { get; set; } [JsonProperty("message")] public string Message { get; set; } public static DgLabMessage Create(string type, string clientId, string targetId, string message) { DgLabMessage dgLabMessage = new DgLabMessage(); dgLabMessage.Type = type; dgLabMessage.ClientId = clientId; dgLabMessage.TargetId = targetId; dgLabMessage.Message = message; return dgLabMessage; } public string ToJson() { return JsonConvert.SerializeObject((object)this); } } internal sealed class StrengthState { public int ACurrent; public int BCurrent; public int AMax; public int BMax; public StrengthState Clone() { StrengthState strengthState = new StrengthState(); strengthState.ACurrent = ACurrent; strengthState.BCurrent = BCurrent; strengthState.AMax = AMax; strengthState.BMax = BMax; return strengthState; } } internal sealed class DGLabSocketServer { internal sealed class AppSocketBehavior : WebSocketBehavior { public DGLabSocketServer Owner { get; set; } public string RemoteEndpoint { get { try { return (((WebSocketBehavior)this).Context != null && ((WebSocketBehavior)this).Context.UserEndPoint != null) ? ((WebSocketBehavior)this).Context.UserEndPoint.ToString() : ""; } catch { return ""; } } } protected override void OnOpen() { Owner.OnOpen(this); } protected override void OnMessage(MessageEventArgs e) { Owner.OnMessage(this, e.Data); } protected override void OnClose(CloseEventArgs e) { Owner.OnClose(this); } public void SendText(string text) { ((WebSocketBehavior)this).Send(text); } } private static readonly Regex StrengthRegex = new Regex("^strength-(\\d+)\\+(\\d+)\\+(\\d+)\\+(\\d+)$", RegexOptions.Compiled); private static readonly Regex SafeCodeRegex = new Regex("[^A-Za-z0-9_-]", RegexOptions.Compiled); private readonly object _sync = new object(); private string _terminalId; private WebSocketServer _server; private WebSocket _remoteSocket; private AppSocketBehavior _session; private string _appId; private bool _bound; private bool _remoteConnected; private bool _remoteClientIdAssigned; private DateTime _lastHeartbeatUtc; private StrengthState _strength; private string _lastError; private string _lastRemoteEndpoint; private string _remoteStatus; internal string ClientId { get { lock (_sync) { return _terminalId; } } } internal bool IsRunning { get { if (IsRemoteMode()) { lock (_sync) { return _remoteSocket != null && _remoteConnected; } } if (_server != null) { return _server.IsListening; } return false; } } internal bool IsConnected { get { lock (_sync) { if (IsRemoteMode()) { return _remoteSocket != null && _remoteConnected; } return _session != null; } } } internal bool IsBound { get { lock (_sync) { return _bound && (IsRemoteMode() ? _remoteConnected : (_session != null)); } } } internal StrengthState Strength { get { lock (_sync) { return _strength.Clone(); } } } internal string LastError { get { lock (_sync) { return _lastError; } } } internal string LastRemoteEndpoint { get { lock (_sync) { return _lastRemoteEndpoint; } } } internal DGLabSocketServer() { _terminalId = Guid.NewGuid().ToString(); _appId = ""; _strength = new StrengthState(); _lastError = ""; _lastRemoteEndpoint = ""; _remoteStatus = ""; } internal void Start() { if (!IsRunning) { if (IsRemoteMode()) { StartRemoteClient(); } else { StartLocalServer(); } } } private void StartLocalServer() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown int num = Clamp(ModConfig.Port.Value, 1, 65535); try { _server = new WebSocketServer(IPAddress.Any, num); _server.AddWebSocketService<AppSocketBehavior>("/", (Action<AppSocketBehavior>)delegate(AppSocketBehavior behavior) { behavior.Owner = this; }); _server.AddWebSocketService<AppSocketBehavior>("/" + _terminalId, (Action<AppSocketBehavior>)delegate(AppSocketBehavior behavior) { behavior.Owner = this; }); _server.Start(); _lastHeartbeatUtc = DateTime.UtcNow; lock (_sync) { _lastError = ""; _remoteStatus = ""; } Plugin.Log.LogInfo((object)("DG-LAB local Socket server listening on port " + num + " with terminalId " + _terminalId)); } catch (Exception ex) { _server = null; lock (_sync) { _lastError = ex.Message; } Plugin.Log.LogError((object)("Failed to start DG-LAB Socket server on port " + num + ": " + ex.Message)); } } private void StartRemoteClient() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown string uri = NormalizeBaseUri(ModConfig.RemoteServerUri.Value); if (string.IsNullOrEmpty(uri)) { lock (_sync) { _lastError = "远程 Socket 服务地址为空。"; _remoteStatus = _lastError; return; } } if (ModConfig.ConnectionMode.Value == SocketConnectionMode.RelayCode) { string text = SafeRelayCode(ModConfig.RemotePairCode.Value); if (!string.IsNullOrEmpty(text)) { lock (_sync) { _terminalId = text; } } } try { _remoteSocket = new WebSocket(uri, new string[0]); _remoteSocket.OnOpen += delegate { lock (_sync) { _remoteConnected = true; _remoteClientIdAssigned = ModConfig.ConnectionMode.Value == SocketConnectionMode.RelayCode && !string.IsNullOrEmpty(SafeRelayCode(ModConfig.RemotePairCode.Value)); _bound = false; _appId = ""; _lastError = ""; _remoteStatus = "已连接远程服务,等待终端 ID/绑定。"; _lastRemoteEndpoint = uri; } Plugin.Log.LogInfo((object)("Connected to remote DG-LAB Socket service: " + uri)); }; _remoteSocket.OnMessage += delegate(object sender, MessageEventArgs e) { HandleRemoteMessage(e.Data); }; _remoteSocket.OnClose += delegate(object sender, CloseEventArgs e) { lock (_sync) { _remoteConnected = false; _remoteClientIdAssigned = false; _bound = false; _appId = ""; _strength = new StrengthState(); _remoteStatus = "远程服务已断开:" + e.Reason; } Plugin.Log.LogInfo((object)("Remote DG-LAB Socket closed: " + e.Reason)); }; _remoteSocket.OnError += delegate(object sender, ErrorEventArgs e) { lock (_sync) { _lastError = e.Message; _remoteStatus = "远程服务错误:" + e.Message; } Plugin.Log.LogWarning((object)("Remote DG-LAB Socket error: " + e.Message)); }; _remoteSocket.ConnectAsync(); } catch (Exception ex) { _remoteSocket = null; lock (_sync) { _lastError = ex.Message; _remoteStatus = "远程服务启动失败:" + ex.Message; } Plugin.Log.LogError((object)("Failed to connect remote DG-LAB Socket service: " + ex.Message)); } } internal void Stop() { lock (_sync) { _bound = false; _session = null; _appId = ""; _lastRemoteEndpoint = ""; _remoteConnected = false; _remoteClientIdAssigned = false; } if (_remoteSocket != null) { try { _remoteSocket.Close(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to close remote DG-LAB Socket: " + ex.Message)); } _remoteSocket = null; } if (_server != null) { try { _server.Stop(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Failed to stop DG-LAB Socket server: " + ex2.Message)); } _server = null; } } internal void Tick() { if (!IsBound) { return; } int num = Math.Max(5000, ModConfig.HeartbeatIntervalMs.Value); if ((DateTime.UtcNow - _lastHeartbeatUtc).TotalMilliseconds >= (double)num) { if (IsRemoteMode()) { SendRemoteRaw("heartbeat", "200"); } else { SendEnvelope("heartbeat", "200"); } _lastHeartbeatUtc = DateTime.UtcNow; } } internal string GetQrUrl() { return "https://www.dungeon-lab.com/app-download.php#DGLAB-SOCKET#" + GetSocketBaseUri() + "/" + ClientId; } internal string GetSocketBaseUri() { SocketConnectionMode value = ModConfig.ConnectionMode.Value; if (value == SocketConnectionMode.RemoteServer || value == SocketConnectionMode.RelayCode) { string text = NormalizeBaseUri(ModConfig.RemoteServerUri.Value); if (!string.IsNullOrEmpty(text)) { return text; } return "ws://127.0.0.1:" + Clamp(ModConfig.Port.Value, 1, 65535); } string text2 = NormalizeBaseUri(ModConfig.PublishSocketUri.Value); if (value == SocketConnectionMode.PublicSocket && !string.IsNullOrEmpty(text2)) { return text2; } if (!string.IsNullOrEmpty(text2)) { return text2; } return "ws://" + GetAdvertiseHost() + ":" + Clamp(ModConfig.Port.Value, 1, 65535); } internal string GetAdvertiseHost() { string value = ModConfig.AdvertiseHost.Value; if (!string.IsNullOrEmpty(value)) { return value.Trim(); } string[] localIPv4Addresses = GetLocalIPv4Addresses(); if (localIPv4Addresses.Length > 0) { return localIPv4Addresses[0]; } return "127.0.0.1"; } internal string[] GetLocalIPv4Addresses() { List<string> list = new List<string>(); try { IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList; foreach (IPAddress iPAddress in addressList) { if (iPAddress.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(iPAddress)) { list.Add(iPAddress.ToString()); } } } catch { } return list.ToArray(); } internal string LocalAddressSummary() { StringBuilder stringBuilder = new StringBuilder(); string[] localIPv4Addresses = GetLocalIPv4Addresses(); for (int i = 0; i < localIPv4Addresses.Length; i++) { if (i > 0) { stringBuilder.Append(" / "); } stringBuilder.Append(localIPv4Addresses[i]); } if (stringBuilder.Length != 0) { return stringBuilder.ToString(); } return "未检测到局域网 IPv4"; } internal string StatusText() { if (IsRemoteMode()) { string remoteStatus; string lastError; lock (_sync) { remoteStatus = _remoteStatus; lastError = _lastError; } if (IsBound) { return "远程 Socket 已绑定"; } if (IsConnected) { if (!string.IsNullOrEmpty(remoteStatus)) { return remoteStatus; } return "远程 Socket 已连接,等待绑定"; } if (!string.IsNullOrEmpty(lastError)) { return "远程 Socket 未连接:" + lastError; } return "远程 Socket 未连接"; } if (!IsRunning) { string lastError2 = LastError; if (!string.IsNullOrEmpty(lastError2)) { return "服务未启动:" + lastError2; } return "服务未启动"; } if (IsBound) { return "已绑定"; } if (IsConnected) { return "已连接,等待绑定"; } return "等待手机 App 连接"; } internal string ConnectionModeText() { return ModConfig.ConnectionMode.Value switch { SocketConnectionMode.LocalServer => "本地局域网", SocketConnectionMode.PublicSocket => "公网/隧道地址", SocketConnectionMode.RemoteServer => "远程 Socket 后端", _ => "自建 Relay Code", }; } internal void OnOpen(AppSocketBehavior behavior) { lock (_sync) { if (_session != null && !object.ReferenceEquals(_session, behavior)) { behavior.SendText(DgLabMessage.Create("error", "", "", "400").ToJson()); return; } _session = behavior; _bound = false; _appId = Guid.NewGuid().ToString(); _strength = new StrengthState(); _lastRemoteEndpoint = behavior.RemoteEndpoint; } behavior.SendText(DgLabMessage.Create("bind", _appId, "", "targetId").ToJson()); Plugin.Log.LogInfo((object)("DG-LAB app connected; assigned appId " + _appId + " from " + behavior.RemoteEndpoint)); } internal void OnClose(AppSocketBehavior behavior) { lock (_sync) { if (object.ReferenceEquals(_session, behavior)) { _session = null; _bound = false; _appId = ""; _strength = new StrengthState(); _lastRemoteEndpoint = ""; } } Plugin.Log.LogInfo((object)"DG-LAB app disconnected."); } internal void OnMessage(AppSocketBehavior behavior, string raw) { DgLabMessage dgLabMessage; try { dgLabMessage = JsonConvert.DeserializeObject<DgLabMessage>(raw); } catch { behavior.SendText(DgLabMessage.Create("msg", "", "", "403").ToJson()); return; } if (dgLabMessage == null || string.IsNullOrEmpty(dgLabMessage.Type)) { behavior.SendText(DgLabMessage.Create("msg", "", "", "403").ToJson()); } else if (dgLabMessage.Type == "bind") { HandleBind(behavior, dgLabMessage); } else if (dgLabMessage.Type == "msg") { HandleAppMessage(dgLabMessage); } } private void HandleBind(AppSocketBehavior behavior, DgLabMessage message) { bool flag; lock (_sync) { flag = object.ReferenceEquals(_session, behavior) && message.ClientId == _terminalId && message.TargetId == _appId; if (flag) { _bound = true; } } if (flag) { behavior.SendText(DgLabMessage.Create("bind", _terminalId, _appId, "200").ToJson()); Plugin.Log.LogInfo((object)"DG-LAB app bound successfully."); } else { behavior.SendText(DgLabMessage.Create("bind", message.ClientId ?? "", message.TargetId ?? "", "400").ToJson()); } } private void HandleRemoteMessage(string raw) { DgLabMessage dgLabMessage; try { dgLabMessage = JsonConvert.DeserializeObject<DgLabMessage>(raw); } catch { Plugin.Log.LogWarning((object)"Remote DG-LAB Socket sent invalid JSON."); return; } if (dgLabMessage == null || string.IsNullOrEmpty(dgLabMessage.Type)) { return; } if (dgLabMessage.Type == "bind") { HandleRemoteBind(dgLabMessage); } else if (dgLabMessage.Type == "msg") { HandleAppMessage(dgLabMessage); } else if (dgLabMessage.Type == "break") { lock (_sync) { _bound = false; _appId = ""; _remoteStatus = "远程配对已断开:" + dgLabMessage.Message; } } } private void HandleRemoteBind(DgLabMessage message) { lock (_sync) { if (message.Message == "targetId") { if ((ModConfig.ConnectionMode.Value != SocketConnectionMode.RelayCode || string.IsNullOrEmpty(SafeRelayCode(ModConfig.RemotePairCode.Value))) && !string.IsNullOrEmpty(message.ClientId)) { _terminalId = message.ClientId; } _remoteClientIdAssigned = true; _remoteStatus = "已获取远程终端 ID,等待手机扫码绑定。"; } else if (message.Message == "200") { _bound = true; _appId = message.TargetId ?? ""; _lastHeartbeatUtc = DateTime.UtcNow; _remoteStatus = "远程 Socket 已绑定。"; Plugin.Log.LogInfo((object)"Remote DG-LAB app bound successfully."); } else { _remoteStatus = "远程绑定失败:" + message.Message; _lastError = _remoteStatus; } } } private void HandleAppMessage(DgLabMessage message) { Match match = StrengthRegex.Match(message.Message ?? ""); if (!match.Success) { return; } lock (_sync) { _strength.ACurrent = ParseInt(match.Groups[1].Value); _strength.BCurrent = ParseInt(match.Groups[2].Value); _strength.AMax = ParseInt(match.Groups[3].Value); _strength.BMax = ParseInt(match.Groups[4].Value); } } internal bool SendPulse(char channel, IList<string> pulses) { if (pulses == null || pulses.Count == 0) { return false; } int val = Clamp(ModConfig.MaxQueuedPulseItems.Value, 1, 100); int count = Math.Min(val, pulses.Count); string command = "pulse-" + channel + ":" + WaveformEncoder.ToJsonArray(pulses, count); return SendEnvelope("msg", command); } internal bool ClearChannel(int channel) { return SendEnvelope("msg", "clear-" + channel); } internal void ClearAll() { ClearChannel(1); ClearChannel(2); } internal bool SetStrength(int channel, int value) { int num = Clamp(value, 0, 200); return SendEnvelope("msg", "strength-" + channel + "+2+" + num); } private bool SendEnvelope(string type, string command) { if (IsRemoteMode()) { if (type == "msg") { return SendRemoteDirect(command); } return SendRemoteRaw(type, command); } AppSocketBehavior session; string appId; lock (_sync) { if (_session == null || !_bound) { return false; } session = _session; appId = _appId; } if (command != null && command.Length > 1950) { Plugin.Log.LogWarning((object)"DG-LAB command was too long and has been dropped."); return false; } try { session.SendText(DgLabMessage.Create(type, _terminalId, appId, command).ToJson()); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to send DG-LAB command: " + ex.Message)); return false; } } private bool SendRemoteDirect(string command) { if (command != null && command.Length > 1950) { Plugin.Log.LogWarning((object)"DG-LAB command was too long and has been dropped."); return false; } WebSocket remoteSocket; string terminalId; string appId; lock (_sync) { if (_remoteSocket == null || !_remoteConnected || !_bound) { return false; } remoteSocket = _remoteSocket; terminalId = _terminalId; appId = _appId; } try { string text = JsonConvert.SerializeObject((object)new { type = 4, message = command, clientId = terminalId, targetId = appId }); remoteSocket.Send(text); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to send remote DG-LAB command: " + ex.Message)); return false; } } private bool SendRemoteRaw(string type, string command) { WebSocket remoteSocket; string terminalId; string appId; lock (_sync) { if (_remoteSocket == null || !_remoteConnected) { return false; } remoteSocket = _remoteSocket; terminalId = _terminalId; appId = _appId; } try { remoteSocket.Send(DgLabMessage.Create(type, terminalId, appId, command).ToJson()); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to send remote DG-LAB raw message: " + ex.Message)); return false; } } private bool IsRemoteMode() { SocketConnectionMode value = ModConfig.ConnectionMode.Value; if (value != SocketConnectionMode.RemoteServer) { return value == SocketConnectionMode.RelayCode; } return true; } private static string NormalizeBaseUri(string value) { string text = (value ?? "").Trim(); while (text.EndsWith("/", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - 1); } return text; } private static string SafeRelayCode(string value) { string text = (value ?? "").Trim(); if (string.IsNullOrEmpty(text)) { return ""; } return SafeCodeRegex.Replace(text, ""); } private static int Clamp(int value, int min, int max) { if (value < min) { return min; } if (value > max) { return max; } return value; } private static int ParseInt(string value) { if (!int.TryParse(value, out var result)) { return 0; } return result; } } internal enum SocketConnectionMode { LocalServer, PublicSocket, RemoteServer, RelayCode } internal enum StimChannel { A, B } internal enum StimChannelMode { A, B, Both, Alternate } internal enum AutoStrengthMode { Off, MinimumOnly, EventScaled } internal enum StimWaveShape { Smooth, Gradient, Sin } internal enum StimEnvelopeShape { SingleTap, DoubleTap, RampUpHoldDown, SawBurst, Tremor, DeathWave } internal static class ModConfig { internal static ConfigEntry<bool> AutoStartServer; internal static ConfigEntry<SocketConnectionMode> ConnectionMode; internal static ConfigEntry<int> Port; internal static ConfigEntry<string> AdvertiseHost; internal static ConfigEntry<string> PublishSocketUri; internal static ConfigEntry<string> RemoteServerUri; internal static ConfigEntry<string> RemotePairCode; internal static ConfigEntry<int> HeartbeatIntervalMs; internal static ConfigEntry<bool> Armed; internal static ConfigEntry<bool> AutoArmOnBind; internal static ConfigEntry<bool> AllowStrengthControl; internal static ConfigEntry<AutoStrengthMode> AutoStrengthMode; internal static ConfigEntry<int> MinimumChannelStrength; internal static ConfigEntry<int> MaxWaveIntensity; internal static ConfigEntry<int> MaxEventDurationMs; internal static ConfigEntry<int> MaxQueuedPulseItems; internal static ConfigEntry<int> ClearDelayMs; internal static ConfigEntry<bool> ContinuousMode; internal static ConfigEntry<int> ContinuousSendIntervalMs; internal static ConfigEntry<int> ContinuousLookaheadMs; internal static ConfigEntry<int> ContinuousFadeOutMs; internal static ConfigEntry<KeyCode> PanelKey; internal static ConfigEntry<KeyCode> EmergencyStopKey; internal static ConfigEntry<bool> HurtEnabled; internal static ConfigEntry<bool> DeathEnabled; internal static ConfigEntry<bool> LocalFootstepEnabled; internal static ConfigEntry<bool> JumpEnabled; internal static ConfigEntry<bool> LandEnabled; internal static ConfigEntry<bool> SlideEnabled; internal static ConfigEntry<bool> LandJumpEnabled; internal static ConfigEntry<bool> EnemyFootstepEnabled; internal static ConfigEntry<int> HurtCooldownMs; internal static ConfigEntry<int> FootstepCooldownMs; internal static ConfigEntry<int> JumpCooldownMs; internal static ConfigEntry<int> LandCooldownMs; internal static ConfigEntry<int> SlideCooldownMs; internal static ConfigEntry<int> MovementCooldownMs; internal static ConfigEntry<int> DeathCooldownMs; internal static ConfigEntry<bool> FirstFootLeft; internal static ConfigEntry<StimChannel> LeftFootChannel; internal static ConfigEntry<StimChannel> RightFootChannel; internal static ConfigEntry<bool> ResetFootOnIdle; internal static ConfigEntry<int> FootstepIdleResetMs; internal static ConfigEntry<int> FootstepIntensity; internal static ConfigEntry<int> FootstepDurationMs; internal static ConfigEntry<int> FootstepFrequency; internal static ConfigEntry<StimWaveShape> FootstepWaveShape; internal static ConfigEntry<int> FootstepDuplicateWindowMs; internal static ConfigEntry<int> WalkFootstepMinIntervalMs; internal static ConfigEntry<int> RunFootstepIntensity; internal static ConfigEntry<int> RunFootstepDurationMs; internal static ConfigEntry<int> RunFootstepMinIntervalMs; internal static ConfigEntry<bool> FallbackFootstepSupplement; internal static ConfigEntry<int> FallbackFootstepNoAnimationMs; internal static ConfigEntry<bool> RunCadenceSupplement; internal static ConfigEntry<int> RunSupplementIntervalMs; internal static ConfigEntry<int> WalkBaseIntensity; internal static ConfigEntry<int> WalkBaseFrequency; internal static ConfigEntry<StimEnvelopeShape> WalkBaseShape; internal static ConfigEntry<int> WalkStepBumpIntensity; internal static ConfigEntry<int> WalkStepBumpDurationMs; internal static ConfigEntry<int> RunBaseIntensity; internal static ConfigEntry<int> RunBaseFrequency; internal static ConfigEntry<StimEnvelopeShape> RunBaseShape; internal static ConfigEntry<int> RunStepBumpIntensity; internal static ConfigEntry<int> RunStepBumpDurationMs; internal static ConfigEntry<int> JumpIntensity; internal static ConfigEntry<int> JumpDurationMs; internal static ConfigEntry<int> JumpFrequency; internal static ConfigEntry<StimChannelMode> JumpChannelMode; internal static ConfigEntry<int> LandIntensity; internal static ConfigEntry<int> LandDurationMs; internal static ConfigEntry<int> LandFrequency; internal static ConfigEntry<StimChannelMode> LandChannelMode; internal static ConfigEntry<StimEnvelopeShape> LandEnvelope; internal static ConfigEntry<int> SlideIntensity; internal static ConfigEntry<int> SlideDurationMs; internal static ConfigEntry<int> SlideFrequency; internal static ConfigEntry<StimChannelMode> SlideChannelMode; internal static ConfigEntry<StimEnvelopeShape> SlideEnvelope; internal static ConfigEntry<int> SlideBaseIntensity; internal static ConfigEntry<int> SlideBaseFrequency; internal static ConfigEntry<int> HurtMinIntensity; internal static ConfigEntry<int> HurtMaxIntensity; internal static ConfigEntry<float> HurtDamageMultiplier; internal static ConfigEntry<float> HurtAMultiplier; internal static ConfigEntry<float> HurtBMultiplier; internal static ConfigEntry<int> HurtDurationMinMs; internal static ConfigEntry<int> HurtDurationMaxMs; internal static ConfigEntry<int> HurtFrequency; internal static ConfigEntry<StimChannelMode> HurtChannelMode; internal static ConfigEntry<StimEnvelopeShape> HurtEnvelope; internal static ConfigEntry<int> DeathIntensity; internal static ConfigEntry<int> DeathDurationMs; internal static ConfigEntry<int> DeathFrequency; internal static ConfigEntry<StimChannelMode> DeathChannelMode; internal static ConfigEntry<StimWaveShape> DeathAWaveShape; internal static ConfigEntry<StimWaveShape> DeathBWaveShape; internal static ConfigEntry<StimEnvelopeShape> DeathEnvelope; internal static ConfigEntry<bool> DeathClearBeforePulse; internal static ConfigEntry<int> EnemyFootstepIntensity; internal static ConfigEntry<float> EnemyFootstepRange; internal static ConfigEntry<int> TestIntensity; internal static void Bind(ConfigFile config) { AutoStartServer = config.Bind<bool>("Connection", "AutoStartServer", true, "Mod 加载时自动启动 DG-LAB Socket 连接。"); ConnectionMode = config.Bind<SocketConnectionMode>("Connection", "ConnectionMode", SocketConnectionMode.LocalServer, "LocalServer=本机监听;PublicSocket=本机监听但二维码使用公网地址;RemoteServer=连接公网 Socket v2 后端;RelayCode=自建 relay code 兼容模式。"); Port = config.Bind<int>("Connection", "Port", 9999, "本机 DG-LAB App Socket 监听端口。"); AdvertiseHost = config.Bind<string>("Connection", "AdvertiseHost", "", "局域网二维码里展示给手机连接的电脑 IP/域名。留空时自动选择局域网 IPv4。"); PublishSocketUri = config.Bind<string>("Connection", "PublishSocketUri", "", "公网/隧道模式二维码使用的 WebSocket 基础地址,例如 wss://ws.example.com。留空时使用局域网地址。"); RemoteServerUri = config.Bind<string>("Connection", "RemoteServerUri", "", "RemoteServer/RelayCode 模式连接的公网 DG-LAB Socket v2 后端地址,例如 wss://ws.example.com。"); RemotePairCode = config.Bind<string>("Connection", "RemotePairCode", "", "RelayCode 模式使用的自定义终端码。该功能需要兼容自定义码的 relay 服务,不能直接填写 DG-LAB App 原生远程口令。"); HeartbeatIntervalMs = config.Bind<int>("Connection", "HeartbeatIntervalMs", 60000, "发送给 DG-LAB App 或远程服务的心跳间隔,单位毫秒。"); Armed = config.Bind<bool>("Safety", "Enabled", false, "为 false 时,游戏事件不会触发波形。可在面板里点击“启用触发”。"); AutoArmOnBind = config.Bind<bool>("Safety", "AutoArmOnBind", true, "绑定 DG-LAB App 后自动启用游戏事件触发。关闭后需手动点击“启用触发”。"); AllowStrengthControl = config.Bind<bool>("Safety", "AllowStrengthControl", false, "兼容旧配置:开启后等同于 EventScaled 自动调强。"); AutoStrengthMode = config.Bind<AutoStrengthMode>("Safety", "AutoStrengthMode", DGLabPunish.AutoStrengthMode.EventScaled, "Off=不调强;MinimumOnly=只补到最低可感强度;EventScaled=按事件强度设置通道强度。"); MinimumChannelStrength = config.Bind<int>("Safety", "MinimumChannelStrength", 30, "MinimumOnly 模式下 A/B 通道会被补到的最低强度。"); MaxWaveIntensity = config.Bind<int>("Safety", "MaxWaveIntensity", 60, "本 Mod 使用的波形强度上限。DG-LAB 波形强度范围为 0-100。"); MaxEventDurationMs = config.Bind<int>("Safety", "MaxEventDurationMs", 4000, "单次事件波形最长持续时间,单位毫秒。"); MaxQueuedPulseItems = config.Bind<int>("Safety", "MaxQueuedPulseItems", 45, "单次事件最多发送多少条 100ms 波形数据。"); ClearDelayMs = config.Bind<int>("Safety", "ClearDelayMs", 60, "高优先级事件 clear 后等待多久再发送新波形,单位毫秒。"); ContinuousMode = config.Bind<bool>("Safety", "ContinuousMode", true, "开启后使用持续状态机:走路/跑步/滑行期间持续输出基础波形,事件叠加强度。关闭后使用旧版单发事件模式。"); ContinuousSendIntervalMs = config.Bind<int>("Continuous", "SendIntervalMs", 70, "连续模式补包间隔,单位毫秒。"); ContinuousLookaheadMs = config.Bind<int>("Continuous", "LookaheadMs", 100, "连续模式每次生成未来多久的波形,单位毫秒。"); ContinuousFadeOutMs = config.Bind<int>("Continuous", "FadeOutMs", 450, "移动停止后的淡出时间,单位毫秒。"); PanelKey = config.Bind<KeyCode>("Safety", "PanelKey", (KeyCode)112, "打开/关闭游戏内郊狼连接面板。"); EmergencyStopKey = config.Bind<KeyCode>("Safety", "EmergencyStopKey", (KeyCode)105, "清空 A/B 波形队列并将 A/B 通道强度设为 0。"); HurtEnabled = config.Bind<bool>("Events", "HurtEnabled", true, "本地玩家受击时触发波形。"); DeathEnabled = config.Bind<bool>("Events", "DeathEnabled", true, "本地玩家死亡时触发波形。"); LocalFootstepEnabled = config.Bind<bool>("Events", "LocalFootstepEnabled", true, "本地玩家脚步时触发低强度波形。"); JumpEnabled = config.Bind<bool>("Events", "JumpEnabled", true, "本地玩家跳跃时触发低强度波形。"); LandEnabled = config.Bind<bool>("Events", "LandEnabled", true, "本地玩家落地时触发低强度波形。"); SlideEnabled = config.Bind<bool>("Events", "SlideEnabled", true, "本地玩家滑行时触发低强度波形。"); LandJumpEnabled = config.Bind<bool>("Events", "LandJumpEnabled", true, "兼容旧配置:总开关,关闭后跳跃、落地、滑行都不触发。"); EnemyFootstepEnabled = config.Bind<bool>("Events", "EnemyFootstepEnabled", false, "猎人敌人近距离脚步提示,默认关闭。"); HurtCooldownMs = config.Bind<int>("Timing", "HurtCooldownMs", 450, "两次受击波形之间的最短间隔,单位毫秒。"); FootstepCooldownMs = config.Bind<int>("Timing", "FootstepCooldownMs", 120, "两次脚步波形之间的最短间隔,单位毫秒。"); JumpCooldownMs = config.Bind<int>("Timing", "JumpCooldownMs", 250, "两次跳跃波形之间的最短间隔,单位毫秒。"); LandCooldownMs = config.Bind<int>("Timing", "LandCooldownMs", 250, "两次落地波形之间的最短间隔,单位毫秒。"); SlideCooldownMs = config.Bind<int>("Timing", "SlideCooldownMs", 300, "两次滑行波形之间的最短间隔,单位毫秒。"); MovementCooldownMs = config.Bind<int>("Timing", "MovementCooldownMs", 300, "兼容旧配置:旧版跳跃/落地/滑行冷却。"); DeathCooldownMs = config.Bind<int>("Timing", "DeathCooldownMs", 5000, "两次死亡波形之间的最短间隔,单位毫秒。"); FirstFootLeft = config.Bind<bool>("Footstep", "FirstFootLeft", true, "站停重置后第一步是否按左脚处理。默认左脚。"); LeftFootChannel = config.Bind<StimChannel>("Footstep", "LeftFootChannel", StimChannel.A, "左脚对应通道。默认 A。"); RightFootChannel = config.Bind<StimChannel>("Footstep", "RightFootChannel", StimChannel.B, "右脚对应通道。默认 B。"); ResetFootOnIdle = config.Bind<bool>("Footstep", "ResetFootOnIdle", true, "长时间没有脚步后,下一步是否重置为 FirstFootLeft。"); FootstepIdleResetMs = config.Bind<int>("Footstep", "FootstepIdleResetMs", 900, "多久没有脚步后重置左右脚,单位毫秒。"); FootstepIntensity = config.Bind<int>("Footstep", "FootstepIntensity", 20, "本地脚步波形强度。"); FootstepDurationMs = config.Bind<int>("Footstep", "FootstepDurationMs", 250, "本地脚步波形持续时间,单位毫秒。"); FootstepFrequency = config.Bind<int>("Footstep", "FootstepFrequency", 180, "脚步波形频率值。"); FootstepWaveShape = config.Bind<StimWaveShape>("Footstep", "FootstepWaveShape", StimWaveShape.Sin, "脚步波形形状。Sin 更像短促踩踏,Smooth 更平滑。"); FootstepDuplicateWindowMs = config.Bind<int>("Footstep", "FootstepDuplicateWindowMs", 35, "极短时间内重复脚步事件的去重窗口,单位毫秒。"); WalkFootstepMinIntervalMs = config.Bind<int>("Footstep", "WalkFootstepMinIntervalMs", 90, "走路脚步最小发送间隔,单位毫秒。"); RunFootstepIntensity = config.Bind<int>("Footstep", "RunFootstepIntensity", 24, "奔跑脚步波形强度。"); RunFootstepDurationMs = config.Bind<int>("Footstep", "RunFootstepDurationMs", 140, "奔跑脚步波形持续时间,单位毫秒。"); RunFootstepMinIntervalMs = config.Bind<int>("Footstep", "RunFootstepMinIntervalMs", 55, "奔跑脚步最小发送间隔,单位毫秒。"); FallbackFootstepSupplement = config.Bind<bool>("Footstep", "FallbackFootstepSupplement", false, "动画脚步长时间没有触发时,是否允许 PlayerAvatar.Footstep 兜底补发。默认关闭。"); FallbackFootstepNoAnimationMs = config.Bind<int>("Footstep", "FallbackFootstepNoAnimationMs", 450, "多久没有动画脚步后允许兜底补发,单位毫秒。"); RunCadenceSupplement = config.Bind<bool>("Footstep", "RunCadenceSupplement", false, "奔跑时如果动画脚步偏慢,是否按间隔补一拍。默认关闭。"); RunSupplementIntervalMs = config.Bind<int>("Footstep", "RunSupplementIntervalMs", 170, "奔跑补拍间隔,单位毫秒。"); WalkBaseIntensity = config.Bind<int>("ContinuousWalk", "WalkBaseIntensity", 10, "走路持续基础波形强度。"); WalkBaseFrequency = config.Bind<int>("ContinuousWalk", "WalkBaseFrequency", 25, "走路持续基础波形频率。"); WalkBaseShape = config.Bind<StimEnvelopeShape>("ContinuousWalk", "WalkBaseShape", StimEnvelopeShape.Tremor, "走路持续基础波形形状。"); WalkStepBumpIntensity = config.Bind<int>("ContinuousWalk", "WalkStepBumpIntensity", 16, "走路每步叠加强度。"); WalkStepBumpDurationMs = config.Bind<int>("ContinuousWalk", "WalkStepBumpDurationMs", 160, "走路每步叠加强度持续时间。"); RunBaseIntensity = config.Bind<int>("ContinuousRun", "RunBaseIntensity", 15, "奔跑持续基础波形强度。"); RunBaseFrequency = config.Bind<int>("ContinuousRun", "RunBaseFrequency", 260, "奔跑持续基础波形频率。"); RunBaseShape = config.Bind<StimEnvelopeShape>("ContinuousRun", "RunBaseShape", StimEnvelopeShape.SawBurst, "奔跑持续基础波形形状。"); RunStepBumpIntensity = config.Bind<int>("ContinuousRun", "RunStepBumpIntensity", 22, "奔跑每步叠加强度。"); RunStepBumpDurationMs = config.Bind<int>("ContinuousRun", "RunStepBumpDurationMs", 110, "奔跑每步叠加强度持续时间。"); JumpIntensity = config.Bind<int>("Actions", "JumpIntensity", 18, "跳跃波形强度。"); JumpDurationMs = config.Bind<int>("Actions", "JumpDurationMs", 250, "跳跃波形持续时间,单位毫秒
BepInEx\plugins\DGLabPunish\QrCodeGenerator.dll
Decompiled 20 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Codecrete")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) Manuel Bleichenbacher and Project Nayuki (MIT License)")] [assembly: AssemblyDescription("QR Code Generator for .NET – simple, compact and with many examples.\r\n\r\nCore features:\r\n- Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard\r\n- Output formats: Raw modules/pixels of the QR symbol, SVG, XAML path, PNG and BMP files. For other raster bitmap formats, see project home page.\r\n- Encodes numeric and special-alphanumeric text in less space than general text\r\n- Open source code under the permissive MIT License\r\n- Built for .NET Standard 2.0 and therefore runs on most modern .NET platforms (.NET Core, .NET Framework, Mono etc.).\r\n- Example code for WinForms, WPF, ASP.NET, ImageSharp, SkiaSharp and many more\r\n\r\nManual parameters:\r\n- You can specify the minimum and maximum version number allowed, and the library will automatically choose the smallest version in the range that fits the data.\r\n- You can specify the mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one.\r\n- You can specify an error correction level, or optionally allow the library to boost it if it doesn't increase the version number.\r\n- You can create a list of data segments manually and add ECI segments.\r\n\r\nOptional advanced features:\r\n- Long text can be split into multiple linked QR codes (aka Structured Append)\r\n- Encodes Japanese Unicode text in Kanji mode to save a lot of space compared to UTF-8 bytes\r\n- Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts\r\n")] [assembly: AssemblyFileVersion("2.0.1.0")] [assembly: AssemblyInformationalVersion("2.1.0+d75ad20b2f2c04a41120c3e7c7326842fc837fcc")] [assembly: AssemblyProduct("QR Code Generator for .NET")] [assembly: AssemblyTitle("QrCodeGenerator")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/manuelbl/QrCodeGenerator")] [assembly: AssemblyVersion("1.6.0.0")] namespace Net.Codecrete.QrCodeGenerator; public static class BitArrayExtensions { public static void AppendBits(this BitArray bitArray, uint val, int len) { if (len < 0 || len > 31) { throw new ArgumentOutOfRangeException("len", "'len' out of range"); } if (val >> len != 0) { throw new ArgumentOutOfRangeException("val", "'val' out of range"); } int length = bitArray.Length; bitArray.Length = length + len; uint num = (uint)(1 << len - 1); for (int i = length; i < length + len; i++) { if ((val & num) != 0) { bitArray.Set(i, value: true); } num >>= 1; } } public static void AppendData(this BitArray bitArray, BitArray otherArray) { Objects.RequireNonNull(otherArray); int num = bitArray.Length; bitArray.Length = num + otherArray.Length; int num2 = 0; while (num2 < otherArray.Length) { if (otherArray[num2]) { bitArray.Set(num, value: true); } num2++; num++; } } public static uint ExtractBits(this BitArray bitArray, int index, int len) { if (len < 0 || len > 31) { throw new ArgumentOutOfRangeException("len", "'len' out of range"); } if (index < 0 || index + len > bitArray.Length) { throw new ArgumentOutOfRangeException("index", "'index' out of range"); } uint num = 0u; for (int i = 0; i < len; i++) { num <<= 1; if (bitArray.Get(index + i)) { num |= 1u; } } return num; } } public class DataTooLongException : ArgumentException { public DataTooLongException(string message) : base(message) { } } internal class Graphics { private readonly int _size; private readonly bool[,] _modules; internal Graphics(int size, bool[,] modules) { _size = size; _modules = modules; } internal string ToSvgString(int border, string foreground, string background) { if (border < 0) { throw new ArgumentOutOfRangeException("border", "Border must be non-negative"); } int num = _size + border * 2; StringBuilder stringBuilder = new StringBuilder().Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n").Append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n").Append($"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {num} {num}\" stroke=\"none\">\n") .Append("\t<rect width=\"100%\" height=\"100%\" fill=\"" + background + "\"/>\n") .Append("\t<path d=\""); bool[,] modules = CopyModules(); CreatePath(stringBuilder, modules, border); return stringBuilder.Append("\" fill=\"" + foreground + "\"/>\n").Append("</svg>\n").ToString(); } internal string ToGraphicsPath(int border) { if (border < 0) { throw new ArgumentOutOfRangeException("border", "Border must be non-negative"); } bool[,] modules = CopyModules(); StringBuilder stringBuilder = new StringBuilder(); CreatePath(stringBuilder, modules, border); return stringBuilder.ToString(); } internal byte[] ToBmpBitmap(int border, int scale, int foreground, int background) { if (scale < 1) { throw new ArgumentOutOfRangeException("scale", scale, "Scale must be greater than 0."); } if (border < 0) { throw new ArgumentOutOfRangeException("border", border, "Border must be non-negative."); } int num = (_size + 2 * border) * scale; if (num > 32767) { throw new ArgumentOutOfRangeException("scale", "Scale or border too large."); } int num2 = (num - 1 >> 3) + 1; int num3 = (num2 + 3) & -4; int num4 = 62 + num * num3; byte[] array = new byte[num4]; array[0] = 66; array[1] = 77; array[2] = (byte)num4; array[3] = (byte)(num4 >> 8); array[4] = (byte)(num4 >> 16); array[5] = (byte)(num4 >> 24); array[10] = 62; array[14] = 40; array[18] = (byte)num; array[19] = (byte)(num >> 8); array[20] = (byte)(num >> 16); array[21] = (byte)(num >> 24); array[22] = array[18]; array[23] = array[19]; array[24] = array[20]; array[25] = array[21]; array[26] = 1; array[28] = 1; array[38] = 196; array[39] = 14; array[42] = array[38]; array[43] = array[39]; array[54] = (byte)foreground; array[55] = (byte)(foreground >> 8); array[56] = (byte)(foreground >> 16); array[58] = (byte)background; array[59] = (byte)(background >> 8); array[60] = (byte)(background >> 16); int num5 = border * scale; if (border > 0) { int num6 = _size * scale; for (int i = 0; i < num3; i++) { byte b = byte.MaxValue; if (i == num2 - 1) { b = (byte)(255 << (num2 << 3) - num); } else if (i >= num2) { b = 0; } for (int j = 0; j < num5; j++) { array[62 + i + j * num3] = b; array[62 + i + (j + num6 + num5) * num3] = b; } } } for (int j = 0; j < _size; j++) { int num7 = j * scale + num5; for (int i = 0; i < num3; i++) { byte b = 0; for (int k = 0; k < 8; k++) { int num8 = ((i << 3) + k) / scale; if (num8 < num) { b = ((num8 >= border && num8 < _size + border) ? ((byte)(b | (byte)((!_modules[_size - j - 1, num8 - border]) ? ((uint)(1 << 7 - k)) : 0u))) : ((byte)(b | (byte)(1 << 7 - k)))); } } array[62 + i + num7 * num3] = b; } for (int i = 1; i <= scale - 1; i++) { for (int k = 0; k < num3; k++) { array[62 + k + (num7 + i) * num3] = array[62 + k + num7 * num3]; } } } return array; } private static void CreatePath(StringBuilder path, bool[,] modules, int border) { int length = modules.GetLength(0); for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { if (modules[i, j]) { DrawLargestRectangle(path, modules, j, i, border); } } } } private static void DrawLargestRectangle(StringBuilder path, bool[,] modules, int x, int y, int border) { int length = modules.GetLength(0); int num = 1; int num2 = 1; int num3 = 1; int num4 = length; for (int i = y; i < length && modules[i, x]; i++) { int j; for (j = 0; x + j < num4 && modules[i, x + j]; j++) { } int num5 = j * (i - y + 1); if (num5 > num3) { num3 = num5; num = j; num2 = i - y + 1; } num4 = x + j; } if (x != 0 || y != 0) { path.Append(' '); } FormattableString formattableString = $"M{x + border},{y + border}h{num}v{num2}h{-num}z"; path.Append(formattableString.ToString(CultureInfo.InvariantCulture)); ClearRectangle(modules, x, y, num, num2); } private static void ClearRectangle(bool[,] modules, int x, int y, int width, int height) { for (int i = y; i < y + height; i++) { for (int j = x; j < x + width; j++) { modules[i, j] = false; } } } private bool[,] CopyModules() { bool[,] array = new bool[_size, _size]; for (int i = 0; i < _size; i++) { for (int j = 0; j < _size; j++) { array[i, j] = _modules[i, j]; } } return array; } } internal static class Objects { internal static T RequireNonNull<T>(T arg) { if (arg == null) { throw new ArgumentNullException(); } return arg; } } internal sealed class PngBuilder { private static readonly byte[] Signature = new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }; private static readonly uint[] CrcTable = new uint[256] { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u }; private static readonly byte[] IHDR = new byte[4] { 73, 72, 68, 82 }; private static readonly byte[] PLTE = new byte[4] { 80, 76, 84, 69 }; private static readonly byte[] IDAT = new byte[4] { 73, 68, 65, 84 }; private static readonly byte[] IEND = new byte[4] { 73, 69, 78, 68 }; private readonly MemoryStream stream = new MemoryStream(); internal static byte[] ToImage(QrCode qrCode, int scale, int border, int foreground, int background) { int num = (qrCode.Size + border * 2) * scale; PngBuilder pngBuilder = new PngBuilder(); pngBuilder.WriteHeader(num, num, 1, 3); pngBuilder.WritePalette(new int[2] { background, foreground }); pngBuilder.WriteData(CreateBitmap(qrCode, border, scale)); pngBuilder.WriteEnd(); return pngBuilder.GetBytes(); } private static byte[] CreateBitmap(QrCode qrCode, int border, int scale) { int size = qrCode.Size; int num = (size + border * 2) * scale; int num2 = (num + 7) / 8 + 1; byte[] array = new byte[num2 * num]; for (int i = 0; i < size; i++) { int num3 = (border + i) * scale * num2; for (int j = 0; j < size; j++) { if (qrCode.GetModule(j, i)) { int k = (border + j) * scale; for (int num4 = k + scale; k < num4; k++) { int num5 = num3 + k / 8 + 1; array[num5] |= (byte)(128 >>> k % 8); } } } for (int l = 1; l < scale; l++) { Array.Copy(array, num3, array, num3 + l * num2, num2); } } return array; } private byte[] GetBytes() { byte[] array = stream.ToArray(); SetCRC(array); return array; } private void WriteHeader(int width, int height, byte bitDepth, byte colorType) { stream.Write(Signature, 0, Signature.Length); WriteChunkStart(IHDR, 13); WriteIntBigEndian((uint)width); WriteIntBigEndian((uint)height); stream.WriteByte(bitDepth); stream.WriteByte(colorType); stream.WriteByte(0); stream.WriteByte(0); stream.WriteByte(0); WriteChunkEnd(); } private void WritePalette(int[] palette) { WriteChunkStart(PLTE, palette.Length * 3); foreach (int num in palette) { stream.WriteByte((byte)((uint)(num >> 16) & 0xFFu)); stream.WriteByte((byte)((uint)(num >> 8) & 0xFFu)); stream.WriteByte((byte)((uint)num & 0xFFu)); } WriteChunkEnd(); } private void WriteData(byte[] data) { byte[] array = Deflate(data); WriteChunkStart(IDAT, array.Length + 6); stream.WriteByte(120); stream.WriteByte(156); stream.Write(array, 0, array.Length); uint value = CalcAdler32(data, 0, data.Length); WriteIntBigEndian(value); WriteChunkEnd(); } private void WriteEnd() { WriteChunkStart(IEND, 0); WriteChunkEnd(); } private static void SetCRC(byte[] bytes) { int num = Signature.Length; while (num < bytes.Length) { int num2 = (bytes[num] << 24) | (bytes[num + 1] << 16) | (bytes[num + 2] << 8) | bytes[num + 3]; uint num3 = CalcCrc32(bytes, num + 4, num2 + 4); int num4 = num + 8 + num2; bytes[num4] = (byte)(num3 >> 24); bytes[num4 + 1] = (byte)(num3 >> 16); bytes[num4 + 2] = (byte)(num3 >> 8); bytes[num4 + 3] = (byte)num3; num = num4 + 4; } } private void WriteChunkStart(byte[] type, int length) { WriteIntBigEndian((uint)length); stream.Write(type, 0, 4); } private void WriteChunkEnd() { stream.SetLength(stream.Length + 4); stream.Position += 4L; } private void WriteIntBigEndian(uint value) { stream.WriteByte((byte)(value >> 24)); stream.WriteByte((byte)(value >> 16)); stream.WriteByte((byte)(value >> 8)); stream.WriteByte((byte)value); } private static byte[] Deflate(byte[] data) { MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Optimal)) { deflateStream.Write(data, 0, data.Length); } return memoryStream.ToArray(); } private static uint CalcAdler32(byte[] data, int index, int length) { uint num = 1u; uint num2 = 0u; int num3 = index + length; for (int i = index; i < num3; i++) { num = (num + data[i]) % 65521; num2 = (num2 + num) % 65521; } return (num2 << 16) + num; } private static uint CalcCrc32(byte[] data, int index, int length) { uint num = uint.MaxValue; int num2 = index + length; for (int i = index; i < num2; i++) { num = CrcTable[(num ^ data[i]) & 0xFF] ^ (num >> 8); } return num ^ 0xFFFFFFFFu; } } public class QrCode { private struct FinderPenalty { private readonly short[] _runHistory; private readonly int _size; public FinderPenalty(int size) { _size = size; _runHistory = new short[7]; } internal int CountPatterns() { int num = _runHistory[1]; bool flag = num > 0 && _runHistory[2] == num && _runHistory[3] == num * 3 && _runHistory[4] == num && _runHistory[5] == num; return ((flag && _runHistory[0] >= num * 4 && _runHistory[6] >= num) ? 1 : 0) + ((flag && _runHistory[6] >= num * 4 && _runHistory[0] >= num) ? 1 : 0); } internal int TerminateAndCount(bool currentRunColor, int currentRunLength) { if (currentRunColor) { AddHistory(currentRunLength); currentRunLength = 0; } currentRunLength += _size; AddHistory(currentRunLength); return CountPatterns(); } internal void AddHistory(int currentRunLength) { if (_runHistory[0] == 0) { currentRunLength += _size; } Array.Copy(_runHistory, 0, _runHistory, 1, 6); _runHistory[0] = (short)currentRunLength; } } public sealed class Ecc { public static readonly Ecc Low = new Ecc(0, 1u); public static readonly Ecc Medium = new Ecc(1, 0u); public static readonly Ecc Quartile = new Ecc(2, 3u); public static readonly Ecc High = new Ecc(3, 2u); internal static readonly Ecc[] AllValues = new Ecc[4] { Low, Medium, Quartile, High }; public int Ordinal { get; } internal uint FormatBits { get; } private Ecc(int ordinal, uint fb) { Ordinal = ordinal; FormatBits = fb; } } private readonly bool[,] _modules; private readonly bool[,] _isFunction; public const int MinVersion = 1; public const int MaxVersion = 40; private const int PenaltyN1 = 3; private const int PenaltyN2 = 3; private const int PenaltyN3 = 40; private const int PenaltyN4 = 10; private static readonly byte[,] EccCodewordsPerBlock = new byte[4, 41] { { 255, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 }, { 255, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 }, { 255, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 }, { 255, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 } }; private static readonly byte[,] NumErrorCorrectionBlocks = new byte[4, 41] { { 255, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25 }, { 255, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49 }, { 255, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68 }, { 255, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81 } }; public int Version { get; } public int Size { get; } public Ecc ErrorCorrectionLevel { get; } public int Mask { get; } public static QrCode EncodeText(string text, Ecc ecl) { Objects.RequireNonNull(text); Objects.RequireNonNull(ecl); return EncodeSegments(QrSegment.MakeSegments(text), ecl); } public static List<QrCode> EncodeTextInMultipleCodes(string text, Ecc ecl, int version = 29, bool boostEcl = true) { Objects.RequireNonNull(text); Objects.RequireNonNull(ecl); try { QrCode item = EncodeSegments(QrSegmentAdvanced.MakeSegmentsOptimally(text, ecl, version, version), ecl, version, version, -1, boostEcl); return new List<QrCode> { item }; } catch (DataTooLongException) { } return (from segmentList in QrSegmentAdvanced.MakeSegmentsForMultipleCodes(text, ecl, version) select EncodeSegments(segmentList, ecl, version, version, -1, boostEcl)).ToList(); } public static QrCode EncodeBinary(byte[] data, Ecc ecl) { Objects.RequireNonNull(data); Objects.RequireNonNull(ecl); QrSegment item = QrSegment.MakeBytes(data); return EncodeSegments(new List<QrSegment> { item }, ecl); } public static QrCode EncodeSegments(List<QrSegment> segments, Ecc ecl, int minVersion = 1, int maxVersion = 40, int mask = -1, bool boostEcl = true) { Objects.RequireNonNull(segments); Objects.RequireNonNull(ecl); if (minVersion < 1 || minVersion > maxVersion) { throw new ArgumentOutOfRangeException("minVersion", "Invalid value"); } if (maxVersion > 40) { throw new ArgumentOutOfRangeException("maxVersion", "Invalid value"); } if (mask < -1 || mask > 7) { throw new ArgumentOutOfRangeException("mask", "Invalid value"); } int num = minVersion; int totalBits; while (true) { int num2 = GetNumDataCodewords(num, ecl) * 8; totalBits = QrSegment.GetTotalBits(segments, num); if (totalBits != -1 && totalBits <= num2) { break; } if (num >= maxVersion) { string message = "Segment too long"; if (totalBits != -1) { message = $"Data length = {totalBits} bits, Max capacity = {num2} bits"; } throw new DataTooLongException(message); } num++; } Ecc[] allValues = Ecc.AllValues; foreach (Ecc ecc in allValues) { if (boostEcl && totalBits <= GetNumDataCodewords(num, ecc) * 8) { ecl = ecc; } } BitArray bitArray = new BitArray(0); foreach (QrSegment segment in segments) { bitArray.AppendBits(segment.EncodingMode.ModeBits, 4); bitArray.AppendBits((uint)segment.NumChars, segment.EncodingMode.NumCharCountBits(num)); bitArray.AppendData(segment.GetData()); } int num3 = GetNumDataCodewords(num, ecl) * 8; bitArray.AppendBits(0u, Math.Min(4, num3 - bitArray.Length)); bitArray.AppendBits(0u, (8 - bitArray.Length % 8) % 8); uint num4 = 236u; while (bitArray.Length < num3) { bitArray.AppendBits(num4, 8); num4 ^= 0xFDu; } byte[] array = new byte[bitArray.Length / 8]; for (int j = 0; j < bitArray.Length; j++) { if (bitArray.Get(j)) { array[j >> 3] |= (byte)(1 << 7 - (j & 7)); } } return new QrCode(num, ecl, array, mask); } public QrCode(int version, Ecc ecl, byte[] dataCodewords, int mask = -1) { if (version < 1 || version > 40) { throw new ArgumentOutOfRangeException("version", "Version value out of range"); } if (mask < -1 || mask > 7) { throw new ArgumentOutOfRangeException("mask", "Mask value out of range"); } Version = version; Size = version * 4 + 17; Objects.RequireNonNull(ecl); ErrorCorrectionLevel = ecl; Objects.RequireNonNull(dataCodewords); _modules = new bool[Size, Size]; _isFunction = new bool[Size, Size]; DrawFunctionPatterns(); byte[] data = AddEccAndInterleave(dataCodewords); DrawCodewords(data); if (mask == -1) { int num = int.MaxValue; for (uint num2 = 0u; num2 < 8; num2++) { ApplyMask(num2); DrawFormatBits(num2); int penaltyScore = GetPenaltyScore(); if (penaltyScore < num) { mask = (int)num2; num = penaltyScore; } ApplyMask(num2); } } Mask = mask; ApplyMask((uint)mask); DrawFormatBits((uint)mask); _isFunction = null; } public bool GetModule(int x, int y) { if (0 <= x && x < Size && 0 <= y && y < Size) { return _modules[y, x]; } return false; } public string ToSvgString(int border) { return ToSvgString(border, "#000000", "#ffffff"); } public string ToSvgString(int border, string foreground, string background) { return AsGraphics().ToSvgString(border, foreground, background); } public string ToGraphicsPath(int border = 0) { return AsGraphics().ToGraphicsPath(border); } public byte[] ToBmpBitmap(int border, int scale, int foreground, int background) { return AsGraphics().ToBmpBitmap(border, scale, foreground, background); } public byte[] ToBmpBitmap(int border = 0, int scale = 1) { return ToBmpBitmap(border, scale, 0, 16777215); } public byte[] ToPngBitmap(int border, int scale, int foreground, int background) { return PngBuilder.ToImage(this, scale, border, foreground, background); } public byte[] ToPngBitmap(int border = 0, int scale = 1) { return PngBuilder.ToImage(this, scale, border, 0, 16777215); } public int RgbColor(byte red, byte green, byte blue) { return (red << 16) | (green << 8) | blue; } private void DrawFunctionPatterns() { for (int i = 0; i < Size; i++) { SetFunctionModule(6, i, i % 2 == 0); SetFunctionModule(i, 6, i % 2 == 0); } DrawFinderPattern(3, 3); DrawFinderPattern(Size - 4, 3); DrawFinderPattern(3, Size - 4); int[] alignmentPatternPositions = GetAlignmentPatternPositions(); int num = alignmentPatternPositions.Length; for (int j = 0; j < num; j++) { for (int k = 0; k < num; k++) { if ((j != 0 || k != 0) && (j != 0 || k != num - 1) && (j != num - 1 || k != 0)) { DrawAlignmentPattern(alignmentPatternPositions[j], alignmentPatternPositions[k]); } } } DrawFormatBits(0u); DrawVersion(); } private void DrawFormatBits(uint mask) { uint num = (ErrorCorrectionLevel.FormatBits << 3) | mask; uint num2 = num; for (int i = 0; i < 10; i++) { num2 = (num2 << 1) ^ ((num2 >> 9) * 1335); } uint x = ((num << 10) | num2) ^ 0x5412u; for (int j = 0; j <= 5; j++) { SetFunctionModule(8, j, GetBit(x, j)); } SetFunctionModule(8, 7, GetBit(x, 6)); SetFunctionModule(8, 8, GetBit(x, 7)); SetFunctionModule(7, 8, GetBit(x, 8)); for (int k = 9; k < 15; k++) { SetFunctionModule(14 - k, 8, GetBit(x, k)); } for (int l = 0; l < 8; l++) { SetFunctionModule(Size - 1 - l, 8, GetBit(x, l)); } for (int m = 8; m < 15; m++) { SetFunctionModule(8, Size - 15 + m, GetBit(x, m)); } SetFunctionModule(8, Size - 8, isDark: true); } private void DrawVersion() { if (Version >= 7) { uint num = (uint)Version; for (int i = 0; i < 12; i++) { num = (num << 1) ^ ((num >> 11) * 7973); } uint x = (uint)(Version << 12) | num; for (int j = 0; j < 18; j++) { bool bit = GetBit(x, j); int num2 = Size - 11 + j % 3; int num3 = j / 3; SetFunctionModule(num2, num3, bit); SetFunctionModule(num3, num2, bit); } } } private void DrawFinderPattern(int x, int y) { for (int i = -4; i <= 4; i++) { for (int j = -4; j <= 4; j++) { int num = Math.Max(Math.Abs(j), Math.Abs(i)); int num2 = x + j; int num3 = y + i; if (0 <= num2 && num2 < Size && 0 <= num3 && num3 < Size) { SetFunctionModule(num2, num3, num != 2 && num != 4); } } } } private void DrawAlignmentPattern(int x, int y) { for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { SetFunctionModule(x + j, y + i, Math.Max(Math.Abs(j), Math.Abs(i)) != 1); } } } private void SetFunctionModule(int x, int y, bool isDark) { _modules[y, x] = isDark; _isFunction[y, x] = true; } private Graphics AsGraphics() { return new Graphics(Size, _modules); } private byte[] AddEccAndInterleave(byte[] data) { Objects.RequireNonNull(data); if (data.Length != GetNumDataCodewords(Version, ErrorCorrectionLevel)) { throw new ArgumentOutOfRangeException("data", "Length of data does not match version and ecl"); } int num = NumErrorCorrectionBlocks[ErrorCorrectionLevel.Ordinal, Version]; int num2 = EccCodewordsPerBlock[ErrorCorrectionLevel.Ordinal, Version]; int num3 = GetNumRawDataModules(Version) / 8; int num4 = num - num3 % num; int num5 = num3 / num; byte[][] array = new byte[num][]; ReedSolomonGenerator reedSolomonGenerator = new ReedSolomonGenerator(num2); int i = 0; int num6 = 0; for (; i < num; i++) { byte[] array2 = CopyOfRange(data, num6, num6 + num5 - num2 + ((i >= num4) ? 1 : 0)); num6 += array2.Length; byte[] array3 = CopyOf(array2, num5 + 1); byte[] remainder = reedSolomonGenerator.GetRemainder(array2); Array.Copy(remainder, 0, array3, array3.Length - num2, remainder.Length); array[i] = array3; } byte[] array4 = new byte[num3]; int j = 0; int num7 = 0; for (; j < array[0].Length; j++) { for (int k = 0; k < array.Length; k++) { if (j != num5 - num2 || k >= num4) { array4[num7] = array[k][j]; num7++; } } } return array4; } private void DrawCodewords(byte[] data) { Objects.RequireNonNull(data); if (data.Length != GetNumRawDataModules(Version) / 8) { throw new ArgumentOutOfRangeException("data", "data length does not match version"); } int num = 0; for (int num2 = Size - 1; num2 >= 1; num2 -= 2) { if (num2 == 6) { num2 = 5; } for (int i = 0; i < Size; i++) { for (int j = 0; j < 2; j++) { int num3 = num2 - j; int num4 = ((((num2 + 1) & 2) == 0) ? (Size - 1 - i) : i); if (!_isFunction[num4, num3] && num < data.Length * 8) { _modules[num4, num3] = GetBit(data[num >>> 3], 7 - (num & 7)); num++; } } } } } private void ApplyMask(uint mask) { if (mask < 0 || mask > 7) { throw new ArgumentOutOfRangeException("mask", "Mask value out of range"); } for (int i = 0; i < Size; i++) { for (int j = 0; j < Size; j++) { bool flag = false; switch (mask) { case 0u: flag = (j + i) % 2 == 0; break; case 1u: flag = i % 2 == 0; break; case 2u: flag = j % 3 == 0; break; case 3u: flag = (j + i) % 3 == 0; break; case 4u: flag = (j / 3 + i / 2) % 2 == 0; break; case 5u: flag = j * i % 2 + j * i % 3 == 0; break; case 6u: flag = (j * i % 2 + j * i % 3) % 2 == 0; break; case 7u: flag = ((j + i) % 2 + j * i % 3) % 2 == 0; break; } _modules[i, j] ^= flag && !_isFunction[i, j]; } } } private int GetPenaltyScore() { int num = 0; for (int i = 0; i < Size; i++) { bool flag = false; int num2 = 0; FinderPenalty finderPenalty = new FinderPenalty(Size); for (int j = 0; j < Size; j++) { if (_modules[i, j] == flag) { num2++; if (num2 == 5) { num += 3; } else if (num2 > 5) { num++; } } else { finderPenalty.AddHistory(num2); if (!flag) { num += finderPenalty.CountPatterns() * 40; } flag = _modules[i, j]; num2 = 1; } } num += finderPenalty.TerminateAndCount(flag, num2) * 40; } for (int k = 0; k < Size; k++) { bool flag2 = false; int num3 = 0; FinderPenalty finderPenalty2 = new FinderPenalty(Size); for (int l = 0; l < Size; l++) { if (_modules[l, k] == flag2) { num3++; if (num3 == 5) { num += 3; } else if (num3 > 5) { num++; } } else { finderPenalty2.AddHistory(num3); if (!flag2) { num += finderPenalty2.CountPatterns() * 40; } flag2 = _modules[l, k]; num3 = 1; } } num += finderPenalty2.TerminateAndCount(flag2, num3) * 40; } for (int m = 0; m < Size - 1; m++) { for (int n = 0; n < Size - 1; n++) { bool flag3 = _modules[m, n]; if (flag3 == _modules[m, n + 1] && flag3 == _modules[m + 1, n] && flag3 == _modules[m + 1, n + 1]) { num += 3; } } } int num4 = 0; for (int num5 = 0; num5 < Size; num5++) { for (int num6 = 0; num6 < Size; num6++) { if (_modules[num5, num6]) { num4++; } } } int num7 = Size * Size; int num8 = (Math.Abs(num4 * 20 - num7 * 10) + num7 - 1) / num7 - 1; return num + num8 * 10; } private int[] GetAlignmentPatternPositions() { if (Version == 1) { return Array.Empty<int>(); } int num = Version / 7 + 2; int num2 = (Version * 8 + num * 3 + 5) / (num * 4 - 4) * 2; int[] array = new int[num]; array[0] = 6; int num3 = array.Length - 1; int num4 = Size - 7; while (num3 >= 1) { array[num3] = num4; num3--; num4 -= num2; } return array; } private static int GetNumRawDataModules(int ver) { if (ver < 1 || ver > 40) { throw new ArgumentOutOfRangeException("ver", "Version number out of range"); } int num = ver * 4 + 17; int num2 = num * num; num2 -= 192; num2 -= 31; num2 -= (num - 16) * 2; if (ver >= 2) { int num3 = ver / 7 + 2; num2 -= (num3 - 1) * (num3 - 1) * 25; num2 -= (num3 - 2) * 2 * 20; if (ver >= 7) { num2 -= 36; } } return num2; } public static int GetNumDataCodewords(int ver, Ecc ecl) { return GetNumRawDataModules(ver) / 8 - EccCodewordsPerBlock[ecl.Ordinal, ver] * NumErrorCorrectionBlocks[ecl.Ordinal, ver]; } private static byte[] CopyOfRange(byte[] original, int from, int to) { byte[] array = new byte[to - from]; Array.Copy(original, from, array, 0, to - from); return array; } private static byte[] CopyOf(byte[] original, int newLength) { byte[] array = new byte[newLength]; Array.Copy(original, array, Math.Min(original.Length, newLength)); return array; } private static bool GetBit(uint x, int i) { return ((x >> i) & 1) != 0; } } public class QrSegment { public sealed class Mode { public static readonly Mode Numeric = new Mode(1u, 10, 12, 14); public static readonly Mode Alphanumeric = new Mode(2u, 9, 11, 13); public static readonly Mode Byte = new Mode(4u, 8, 16, 16); public static readonly Mode Kanji = new Mode(8u, 8, 10, 12); public static readonly Mode Eci = new Mode(7u, default(int), default(int), default(int)); public static readonly Mode StructuredAppend = new Mode(3u, default(int), default(int), default(int)); internal uint ModeBits { get; } private int[] NumBitsCharCount { get; } internal int NumCharCountBits(int ver) { return NumBitsCharCount[(ver + 7) / 17]; } private Mode(uint modeBits, params int[] numBitsCharCount) { ModeBits = modeBits; NumBitsCharCount = numBitsCharCount; } } private readonly BitArray _data; private static readonly Regex NumericRegex = new Regex("^[0-9]*$", RegexOptions.Compiled); private static readonly Regex AlphanumericRegex = new Regex("^[A-Z0-9 $%*+./:-]*$", RegexOptions.Compiled); internal const string AlphanumericCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; public Mode EncodingMode { get; } public int NumChars { get; } public static QrSegment MakeBytes(byte[] data) { Objects.RequireNonNull(data); BitArray bitArray = new BitArray(0); foreach (byte val in data) { bitArray.AppendBits(val, 8); } return new QrSegment(Mode.Byte, data.Length, bitArray); } public static QrSegment MakeBytes(ArraySegment<byte> data) { Objects.RequireNonNull(data); BitArray bitArray = new BitArray(0); foreach (byte item in (IEnumerable<byte>)data) { bitArray.AppendBits(item, 8); } return new QrSegment(Mode.Byte, data.Count, bitArray); } public static QrSegment MakeNumeric(string digits) { Objects.RequireNonNull(digits); if (!IsNumeric(digits)) { throw new ArgumentOutOfRangeException("digits", "String contains non-numeric characters"); } BitArray bitArray = new BitArray(0); int num; for (int i = 0; i < digits.Length; i += num) { num = Math.Min(digits.Length - i, 3); bitArray.AppendBits(uint.Parse(digits.Substring(i, num)), num * 3 + 1); } return new QrSegment(Mode.Numeric, digits.Length, bitArray); } public static QrSegment MakeAlphanumeric(string text) { Objects.RequireNonNull(text); if (!IsAlphanumeric(text)) { throw new ArgumentOutOfRangeException("text", "String contains unencodable characters in alphanumeric mode"); } BitArray bitArray = new BitArray(0); int i; for (i = 0; i <= text.Length - 2; i += 2) { uint num = (uint)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".IndexOf(text[i]) * 45); num += (uint)"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".IndexOf(text[i + 1]); bitArray.AppendBits(num, 11); } if (i < text.Length) { bitArray.AppendBits((uint)"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".IndexOf(text[i]), 6); } return new QrSegment(Mode.Alphanumeric, text.Length, bitArray); } internal static QrSegment MakeStructuredAppend(byte parity, int position, int total) { BitArray bitArray = new BitArray(0); bitArray.AppendBits((uint)(position - 1), 4); bitArray.AppendBits((uint)(total - 1), 4); bitArray.AppendBits(parity, 8); return new QrSegment(Mode.StructuredAppend, 0, bitArray); } public static List<QrSegment> MakeSegments(string text) { Objects.RequireNonNull(text); List<QrSegment> list = new List<QrSegment>(); if (!(text == "")) { if (IsNumeric(text)) { list.Add(MakeNumeric(text)); } else if (IsAlphanumeric(text)) { list.Add(MakeAlphanumeric(text)); } else { list.Add(MakeBytes(Encoding.UTF8.GetBytes(text))); } } return list; } public static QrSegment MakeEci(int assignVal) { BitArray bitArray = new BitArray(0); if (assignVal < 0) { throw new ArgumentOutOfRangeException("assignVal", "ECI assignment value out of range"); } if (assignVal < 128) { bitArray.AppendBits((uint)assignVal, 8); } else if (assignVal < 16384) { bitArray.AppendBits(2u, 2); bitArray.AppendBits((uint)assignVal, 14); } else { if (assignVal >= 1000000) { throw new ArgumentOutOfRangeException("assignVal", "ECI assignment value out of range"); } bitArray.AppendBits(6u, 3); bitArray.AppendBits((uint)assignVal, 21); } return new QrSegment(Mode.Eci, 0, bitArray); } public static bool IsNumeric(string text) { return NumericRegex.IsMatch(text); } public static bool IsAlphanumeric(string text) { return AlphanumericRegex.IsMatch(text); } public QrSegment(Mode mode, int numChars, BitArray data) { EncodingMode = Objects.RequireNonNull(mode); Objects.RequireNonNull(data); if (numChars < 0) { throw new ArgumentOutOfRangeException("numChars", "Invalid value"); } NumChars = numChars; _data = (BitArray)data.Clone(); } public BitArray GetData() { return (BitArray)_data.Clone(); } public string GetText() { StringBuilder stringBuilder = new StringBuilder(); AppendText(stringBuilder); return stringBuilder.ToString(); } public static string GetJoinedText(List<QrSegment> segments) { StringBuilder stringBuilder = new StringBuilder(); foreach (QrSegment segment in segments) { segment.AppendText(stringBuilder); } return stringBuilder.ToString(); } public static string GetJoinedText(List<List<QrSegment>> segments) { StringBuilder stringBuilder = new StringBuilder(); foreach (List<QrSegment> segment in segments) { foreach (QrSegment item in segment) { item.AppendText(stringBuilder); } } return stringBuilder.ToString(); } private void AppendText(StringBuilder builder) { if (EncodingMode == Mode.Numeric) { for (int i = 0; i < NumChars; i += 3) { int num = Math.Min(NumChars - i, 3); int index = i / 3 * 10; switch (num) { case 3: { uint num4 = _data.ExtractBits(index, 10); builder.Append((char)(48 + num4 / 100)); builder.Append((char)(48 + num4 / 10 % 10)); builder.Append((char)(48 + num4 % 10)); break; } case 2: { uint num3 = _data.ExtractBits(index, 7); builder.Append((char)(48 + num3 / 10)); builder.Append((char)(48 + num3 % 10)); break; } case 1: { uint num2 = _data.ExtractBits(index, 4); builder.Append((char)(48 + num2)); break; } } } } else if (EncodingMode == Mode.Alphanumeric) { for (int j = 0; j < NumChars; j += 2) { int num5 = Math.Min(NumChars - j, 2); int index2 = j / 2 * 11; if (num5 == 2) { int num6 = (int)_data.ExtractBits(index2, 11); builder.Append("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"[num6 / 45]); builder.Append("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"[num6 % 45]); } else { int index3 = (int)_data.ExtractBits(index2, 6); builder.Append("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"[index3]); } } } else if (EncodingMode == Mode.Byte) { byte[] array = new byte[NumChars]; for (int k = 0; k < NumChars; k++) { array[k] = (byte)_data.ExtractBits(k * 8, 8); } builder.Append(Encoding.UTF8.GetString(array)); } } internal static int GetTotalBits(List<QrSegment> segments, int version) { Objects.RequireNonNull(segments); long num = 0L; foreach (QrSegment segment in segments) { Objects.RequireNonNull(segment); int num2 = segment.EncodingMode.NumCharCountBits(version); if (segment.NumChars >= 1 << num2) { return -1; } num += 4L + (long)num2 + segment._data.Length; if (num > int.MaxValue) { return -1; } } return (int)num; } internal static int GetTotalBits(int numChars, Mode mode, int version) { int num = mode.NumCharCountBits(version); if (numChars >= 1 << num) { return -1; } int num2 = 0; if (mode == Mode.Numeric) { num2 = numChars / 3 * 10 + ((numChars % 3 != 0) ? (numChars % 3 * 3 + 1) : 0); } else if (mode == Mode.Alphanumeric) { num2 = numChars / 2 * 11 + ((numChars % 2 != 0) ? 6 : 0); } else if (mode == Mode.Byte) { num2 = numChars * 8; } else { if (mode != Mode.Kanji) { throw new ArgumentOutOfRangeException("mode", "Unsupported mode for this calculation"); } num2 = numChars * 13; } return 4 + num + num2; } } public static class QrSegmentAdvanced { private const string PackedQrKanjiToUnicode = "MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZAALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA/////////////////////////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBNME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDhMOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQmBCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQJRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32tfe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYgZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLsbTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CElFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKgdZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6rXydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOnZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtkmANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiVnfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/XYB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6lufhQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/Sge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZWXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJacl5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bbfL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333PfZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7egM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bjWMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oCiuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhYWVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLrW8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/DfQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGroXmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16cYBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851sTk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04HYWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5fUbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+KgACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHMW+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3vlzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/lUBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGTUZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQUw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaPVqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrjWtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yrXLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQYClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FYYVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KUYtdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTHZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2ZoZl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGgeaEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammyaa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzXbMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7Jbrdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3AwcD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKycsNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTydPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYwdjt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXogeh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuce5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5afnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAhgCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHCgbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMvgyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UYhSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZiF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tfi2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3bjcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEykTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNWk7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYuli+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJeml6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnYmdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uDm5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1knVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////w=="; private static readonly ushort[] UnicodeToQrKanji; public static List<QrSegment> MakeSegmentsOptimally(string text, QrCode.Ecc ecl, int minVersion = 1, int maxVersion = 40) { Objects.RequireNonNull(text); Objects.RequireNonNull(ecl); if (minVersion < 1 || minVersion > maxVersion) { throw new ArgumentOutOfRangeException("minVersion", "Invalid value"); } if (maxVersion > 40) { throw new ArgumentOutOfRangeException("maxVersion", "Invalid value"); } List<QrSegment> list = null; int[] codePoints = ToCodePoints(text); int num = 0; int num2 = 0; for (int i = minVersion; i <= maxVersion; i++) { if (i == minVersion || i == 10 || i == 27) { list = MakeSegmentsOptimally(codePoints, i); } num = QrCode.GetNumDataCodewords(i, ecl) * 8; num2 = QrSegment.GetTotalBits(list, i); if (num2 != -1 && num2 <= num) { return list; } } string message = "Segment too long"; if (num2 != -1) { message = $"Data length = {num2} bits, Max capacity = {num} bits"; } throw new DataTooLongException(message); } private static List<QrSegment> MakeSegmentsOptimally(IReadOnlyList<int> codePoints, int version) { if (codePoints.Count == 0) { return new List<QrSegment>(); } QrSegment.Mode[] charModes = ComputeCharacterModes(codePoints, version); return SplitIntoSegments(codePoints, charModes); } private static int MeasureSegmentsOptimally(IReadOnlyList<int> codePoints, int version) { if (codePoints.Count == 0) { return 0; } QrSegment.Mode[] charModes = ComputeCharacterModes(codePoints, version); return MeasureSegments(codePoints, charModes, version); } private static QrSegment.Mode[] ComputeCharacterModes(IReadOnlyList<int> codePoints, int version) { if (codePoints.Count == 0) { throw new ArgumentOutOfRangeException("codePoints"); } QrSegment.Mode[] array = new QrSegment.Mode[4] { QrSegment.Mode.Byte, QrSegment.Mode.Alphanumeric, QrSegment.Mode.Numeric, QrSegment.Mode.Kanji }; int num = array.Length; int[] array2 = new int[num]; for (int i = 0; i < num; i++) { array2[i] = (4 + array[i].NumCharCountBits(version)) * 6; } QrSegment.Mode[,] array3 = new QrSegment.Mode[codePoints.Count, num]; int[] array4 = (int[])array2.Clone(); for (int j = 0; j < codePoints.Count; j++) { int num2 = codePoints[j]; int[] array5 = new int[num]; array5[0] = array4[0] + CountUtf8Bytes(num2) * 8 * 6; array3[j, 0] = array[0]; if (Enumerable.Contains("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", (char)num2)) { array5[1] = array4[1] + 33; array3[j, 1] = array[1]; } if (48 <= num2 && num2 <= 57) { array5[2] = array4[2] + 20; array3[j, 2] = array[2]; } if (IsKanji(num2)) { array5[3] = array4[3] + 78; array3[j, 3] = array[3]; } for (int k = 0; k < num; k++) { for (int l = 0; l < num; l++) { int num3 = (array5[l] + 5) / 6 * 6 + array2[k]; if (array3[j, l] != null && (array3[j, k] == null || num3 < array5[k])) { array5[k] = num3; array3[j, k] = array[l]; } } } array4 = array5; } QrSegment.Mode mode = null; int m = 0; int num4 = 0; for (; m < num; m++) { if (mode == null || array4[m] < num4) { num4 = array4[m]; mode = array[m]; } } QrSegment.Mode[] array6 = new QrSegment.Mode[codePoints.Count]; for (int num5 = array6.Length - 1; num5 >= 0; num5--) { for (int n = 0; n < num; n++) { if (array[n] == mode) { mode = (array6[num5] = array3[num5, n]); break; } } } return array6; } private static List<QrSegment> SplitIntoSegments(IReadOnlyList<int> codePoints, IReadOnlyList<QrSegment.Mode> charModes) { if (codePoints.Count == 0) { throw new ArgumentOutOfRangeException("codePoints"); } List<QrSegment> result = new List<QrSegment>(); GroupConsecutiveModes(charModes, delegate(int startIndex, int endIndex, QrSegment.Mode mode) { string text = FromCodePoints(codePoints, startIndex, endIndex - startIndex); if (mode == QrSegment.Mode.Byte) { result.Add(QrSegment.MakeBytes(Encoding.UTF8.GetBytes(text))); } else if (mode == QrSegment.Mode.Numeric) { result.Add(QrSegment.MakeNumeric(text)); } else if (mode == QrSegment.Mode.Alphanumeric) { result.Add(QrSegment.MakeAlphanumeric(text)); } else if (mode == QrSegment.Mode.Kanji) { result.Add(MakeKanji(text)); } }); return result; } private static int MeasureSegments(IReadOnlyList<int> codePoints, IReadOnlyList<QrSegment.Mode> charModes, int version) { if (codePoints.Count == 0) { throw new ArgumentOutOfRangeException("codePoints"); } int result = 0; GroupConsecutiveModes(charModes, delegate(int startIndex, int endIndex, QrSegment.Mode mode) { int num; if (mode == QrSegment.Mode.Byte) { num = 0; for (int i = startIndex; i < endIndex; i++) { num += CountUtf8Bytes(codePoints[i]); } } else { num = endIndex - startIndex; } result += QrSegment.GetTotalBits(num, mode, version); }); return result; } private static string FromCodePoints(IReadOnlyList<int> codepoints, int startIndex, int count) { Encoding encoding = new UTF32Encoding(!BitConverter.IsLittleEndian, byteOrderMark: false, throwOnInvalidCharacters: true); byte[] array = new byte[count * 4]; int num = startIndex; int num2 = 0; while (num < startIndex + count) { byte[] bytes = BitConverter.GetBytes(codepoints[num]); array[num2] = bytes[0]; array[num2 + 1] = bytes[1]; array[num2 + 2] = bytes[2]; array[num2 + 3] = bytes[3]; num++; num2 += 4; } return encoding.GetString(array); } private static int[] ToCodePoints(string s) { byte[] bytes = new UTF32Encoding(!BitConverter.IsLittleEndian, byteOrderMark: false, throwOnInvalidCharacters: true).GetBytes(s); int[] array = new int[bytes.Length / 4]; int num = 0; int num2 = 0; while (num < bytes.Length) { array[num2] = BitConverter.ToInt32(bytes, num); num += 4; num2++; } return array; } private static int CountUtf8Bytes(int cp) { if (cp < 0) { throw new ArgumentOutOfRangeException("cp", "Invalid code point"); } if (cp < 128) { return 1; } if (cp < 2048) { return 2; } if (cp < 65536) { return 3; } if (cp < 1114112) { return 4; } throw new ArgumentOutOfRangeException("cp", "Invalid code point"); } public static QrSegment MakeKanji(string text) { Objects.RequireNonNull(text); BitArray bitArray = new BitArray(0); foreach (ushort item in text.Select((char t) => UnicodeToQrKanji[(uint)t])) { if (item == ushort.MaxValue) { throw new ArgumentOutOfRangeException("text", "String contains non-kanji-mode characters"); } bitArray.AppendBits(item, 13); } return new QrSegment(QrSegment.Mode.Kanji, text.Length, bitArray); } public static bool IsEncodableAsKanji(string text) { Objects.RequireNonNull(text); return text.All((char t) => IsKanji(t)); } private static bool IsKanji(int c) { if (c < UnicodeToQrKanji.Length) { return UnicodeToQrKanji[c] != ushort.MaxValue; } return false; } static QrSegmentAdvanced() { UnicodeToQrKanji = new ushort[65536]; for (int i = 0; i < UnicodeToQrKanji.Length; i++) { UnicodeToQrKanji[i] = ushort.MaxValue; } byte[] array = Convert.FromBase64String("MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZAALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA/////////////////////////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBNME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDhMOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQmBCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQJRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32tfe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYgZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLsbTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CElFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKgdZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6rXydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOnZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtkmANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiVnfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/XYB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6lufhQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/Sge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZWXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJacl5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bbfL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333PfZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7egM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bjWMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oCiuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhYWVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLrW8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/DfQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGroXmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16cYBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851sTk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04HYWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5fUbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+KgACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHMW+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3vlzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/lUBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGTUZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQUw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaPVqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrjWtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yrXLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQYClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FYYVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KUYtdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTHZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2ZoZl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGgeaEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammyaa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzXbMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7Jbrdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3AwcD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKycsNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTydPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYwdjt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXogeh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuce5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5afnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAhgCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHCgbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMvgyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UYhSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZiF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tfi2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3bjcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEykTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNWk7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYuli+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJeml6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnYmdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uDm5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1knVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////w=="); for (int j = 0; j < array.Length; j += 2) { int num = (array[j] << 8) | array[j + 1]; if (num != 65535) { UnicodeToQrKanji[num] = (ushort)(j / 2); } } } public static List<List<QrSegment>> MakeSegmentsForMultipleCodes(string text, QrCode.Ecc ecl, int version = 29) { Objects.RequireNonNull(text); Objects.RequireNonNull(ecl); if (version < 1 || version > 40) { throw new ArgumentOutOfRangeException("version", "Invalid value"); } List<List<QrSegment>> list = SplitTextIntoMultipleCodes(text, ecl, version); byte parity = CalculateParity(text); for (int i = 0; i < list.Count; i++) { list[i].Insert(0, QrSegment.MakeStructuredAppend(parity, i + 1, list.Count)); } return list; } private static List<List<QrSegment>> SplitTextIntoMultipleCodes(string text, QrCode.Ecc ecl, int version) { int[] array = ToCodePoints(text); int num = QrCode.GetNumDataCodewords(version, ecl) * 8 - 20; List<List<QrSegment>> list = new List<List<QrSegment>>(); int num2 = 0; while (num2 < array.Length) { if (list.Count >= 16) { throw new DataTooLongException("The text is too long to fit into 16 QR codes"); } int num3 = num2; int num4 = array.Length; while (num3 < num4) { int num5 = (num3 + num4 + 1) / 2; if (MeasureSegmentsOptimally(new ArraySegment<int>(array, num2, num5 - num2), version) <= num) { num3 = num5; } else { num4 = num5 - 1; } } if (num4 == num2) { throw new InvalidOperationException("QR code splitting: should not reach"); } list.Add(MakeSegmentsOptimally(new ArraySegment<int>(array, num2, num4 - num2), version)); num2 = num4; } return list; } private static byte CalculateParity(string text) { byte[] bytes = Encoding.UTF8.GetBytes(text); byte b = 0; byte[] array = bytes; foreach (byte b2 in array) { b ^= (byte)(b2 >> 8); } return b; } private static void GroupConsecutiveModes(IReadOnlyList<QrSegment.Mode> elements, Action<int, int, QrSegment.Mode> action) { int arg = 0; QrSegment.Mode mode = elements[0]; int num = 0; while (true) { if (num == elements.Count || elements[num] != mode) { action(arg, num, mode); if (num == elements.Count) { break; } arg = num; mode = elements[num]; } num++; } } } internal class ReedSolomonGenerator { private readonly byte[] _coefficients; internal ReedSolomonGenerator(int degree) { if (degree < 1 || degree > 255) { throw new ArgumentOutOfRangeException("degree", "Degree out of range"); } _coefficients = new byte[degree]; _coefficients[degree - 1] = 1; uint num = 1u; for (int i = 0; i < degree; i++) { for (int j = 0; j < _coefficients.Length; j++) { _coefficients[j] = Multiply(_coefficients[j], num); if (j + 1 < _coefficients.Length) { _coefficients[j] ^= _coefficients[j + 1]; } } num = Multiply(num, 2u); } } internal byte[] GetRemainder(byte[] data) { Objects.RequireNonNull(data); byte[] array = new byte[_coefficients.Length]; for (int i = 0; i < data.Length; i++) { uint y = (uint)(data[i] ^ array[0]); Array.Copy(array, 1, array, 0, array.Length - 1); array[^1] = 0; for (int j = 0; j < array.Length; j++) { array[j] ^= Multiply(_coefficients[j], y); } } return array; } private static byte Multiply(uint x, uint y) { uint num = 0u; for (int num2 = 7; num2 >= 0; num2--) { num = (num << 1) ^ ((num >> 7) * 285); num ^= ((y >> num2) & 1) * x; } return (byte)num; } }