C#:
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("QRLogo", "NINJA WORKS", "1.1.1")]
public class QRLogo : RustPlugin
{
private const float ANIMATION_DURATION = 0.2f;
private const float ANIMATION_FPS = 60f;
private const float EASING_POWER = 3.5f;
private const float QR_OFFSET_X_SMALL = -0.005f;
private const float QR_OFFSET_Y_SMALL = -0.005f;
private const float QR_OFFSET_X_EXPANDED = 0f;
private const float QR_OFFSET_Y_EXPANDED = 0f;
private const string SOUND_EFFECT = "assets/bundled/prefabs/fx/notice/item.select.fx.prefab";
private const string UI_LOGO = "QRLogo_Logo";
private const string UI_ANIM_CONTAINER = "QRLogo_AnimContainer";
private const string UI_ANIM_PANEL = "QRLogo_AnimPanel";
private ConfigData _config;
private Dictionary<ulong, Timer> _animationTimers = new Dictionary<ulong, Timer>();
private HashSet<ulong> _expandedPlayers = new HashSet<ulong>();
private bool[,] _qrMatrix;
private class ConfigData
{
public string QrUrl { get; set; }
public float LogoAnchorX { get; set; }
public float LogoAnchorY { get; set; }
public float LogoSizePx { get; set; }
public float ExpandedSizePx { get; set; }
public bool EnableAnimation { get; set; }
public string QrModuleColor { get; set; }
public string QrBackgroundColor { get; set; }
}
protected override void LoadDefaultConfig()
{
_config = new ConfigData
{
QrUrl = "https://codefling.com",
LogoAnchorX = 0.01f,
LogoAnchorY = 0.98f,
LogoSizePx = 80f,
ExpandedSizePx = 500f,
EnableAnimation = true,
QrModuleColor = "0 0 0 1",
QrBackgroundColor = "1 1 1 1"
};
SaveConfig();
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<ConfigData>();
if (_config == null)
{
LoadDefaultConfig();
return;
}
bool changed = false;
if (string.IsNullOrEmpty(_config.QrModuleColor))
{
_config.QrModuleColor = "0 0 0 1";
changed = true;
}
if (string.IsNullOrEmpty(_config.QrBackgroundColor))
{
_config.QrBackgroundColor = "1 1 1 1";
changed = true;
}
if (changed)
SaveConfig();
}
catch
{
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(_config);
private void OnServerInitialized()
{
_qrMatrix = QRCodeGen.Generate(EncodeUrl(_config.QrUrl));
foreach (var player in BasePlayer.activePlayerList)
{
DestroyAllUI(player);
ShowLogoUI(player);
}
}
private void Unload()
{
foreach (var t in _animationTimers.Values)
t?.Destroy();
_animationTimers.Clear();
_expandedPlayers.Clear();
foreach (var player in BasePlayer.activePlayerList)
DestroyAllUI(player);
QRCodeGen.EXP = null;
QRCodeGen.LOG = null;
}
private void OnPlayerConnected(BasePlayer player)
{
if (player == null) return;
CleanupPlayer(player.userID);
timer.Once(1f, () =>
{
if (player == null || !player.IsConnected) return;
DestroyAllUI(player);
ShowLogoUI(player);
});
}
private void OnPlayerDisconnected(BasePlayer player, string reason)
{
if (player == null) return;
CleanupPlayer(player.userID);
}
[ConsoleCommand("qrlogo.expand")]
private void CmdExpand(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null) return;
if (_expandedPlayers.Contains(player.userID)) return;
if (_animationTimers.ContainsKey(player.userID)) return;
StartExpandAnimation(player);
}
[ConsoleCommand("qrlogo.collapse")]
private void CmdCollapse(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null) return;
if (!_expandedPlayers.Contains(player.userID)) return;
StartCollapseAnimation(player);
}
private void DestroyAllUI(BasePlayer player)
{
CuiHelper.DestroyUi(player, UI_LOGO);
CuiHelper.DestroyUi(player, UI_ANIM_CONTAINER);
}
private void ShowLogoUI(BasePlayer player)
{
CuiHelper.DestroyUi(player, UI_LOGO);
float size = _config.LogoSizePx;
var container = new CuiElementContainer();
container.Add(new CuiPanel
{
Image = { Color = _config.QrBackgroundColor },
RectTransform = {
AnchorMin = $"{_config.LogoAnchorX} {_config.LogoAnchorY}",
AnchorMax = $"{_config.LogoAnchorX} {_config.LogoAnchorY}",
OffsetMin = $"0 {-size}",
OffsetMax = $"{size} 0"
}
}, "Hud", UI_LOGO);
DrawQR(container, _qrMatrix, UI_LOGO, QR_OFFSET_X_SMALL, QR_OFFSET_Y_SMALL);
container.Add(new CuiButton
{
Button = { Command = "qrlogo.expand", Color = "0 0 0 0" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, UI_LOGO);
CuiHelper.AddUi(player, container);
}
private void ShowExpandedUI(BasePlayer player, float progress)
{
CuiHelper.DestroyUi(player, UI_ANIM_CONTAINER);
float logoSize = _config.LogoSizePx;
float expandedSize = _config.ExpandedSizePx;
float halfExpanded = expandedSize / 2f;
float currentAnchorX = Mathf.Lerp(_config.LogoAnchorX, 0.5f, progress);
float currentAnchorY = Mathf.Lerp(_config.LogoAnchorY, 0.5f, progress);
float startOffsetMinX = 0f;
float startOffsetMinY = -logoSize;
float startOffsetMaxX = logoSize;
float startOffsetMaxY = 0f;
float endOffsetMinX = -halfExpanded;
float endOffsetMinY = -halfExpanded;
float endOffsetMaxX = halfExpanded;
float endOffsetMaxY = halfExpanded;
float currentOffsetMinX = Mathf.Lerp(startOffsetMinX, endOffsetMinX, progress);
float currentOffsetMinY = Mathf.Lerp(startOffsetMinY, endOffsetMinY, progress);
float currentOffsetMaxX = Mathf.Lerp(startOffsetMaxX, endOffsetMaxX, progress);
float currentOffsetMaxY = Mathf.Lerp(startOffsetMaxY, endOffsetMaxY, progress);
float currentQrOffsetX = Mathf.Lerp(QR_OFFSET_X_SMALL, QR_OFFSET_X_EXPANDED, progress);
float currentQrOffsetY = Mathf.Lerp(QR_OFFSET_Y_SMALL, QR_OFFSET_Y_EXPANDED, progress);
var container = new CuiElementContainer();
container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
CursorEnabled = true
}, "Hud", UI_ANIM_CONTAINER);
container.Add(new CuiButton
{
Button = { Command = "qrlogo.collapse", Color = "0 0 0 0" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, UI_ANIM_CONTAINER);
container.Add(new CuiPanel
{
Image = { Color = _config.QrBackgroundColor },
RectTransform = {
AnchorMin = $"{currentAnchorX} {currentAnchorY}",
AnchorMax = $"{currentAnchorX} {currentAnchorY}",
OffsetMin = $"{currentOffsetMinX} {currentOffsetMinY}",
OffsetMax = $"{currentOffsetMaxX} {currentOffsetMaxY}"
}
}, UI_ANIM_CONTAINER, UI_ANIM_PANEL);
DrawQR(container, _qrMatrix, UI_ANIM_PANEL, currentQrOffsetX, currentQrOffsetY);
container.Add(new CuiButton
{
Button = { Command = "qrlogo.collapse", Color = "0 0 0 0" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, UI_ANIM_PANEL);
CuiHelper.AddUi(player, container);
}
private void StartExpandAnimation(BasePlayer player)
{
StopAnimation(player.userID);
CuiHelper.DestroyUi(player, UI_LOGO);
CuiHelper.DestroyUi(player, UI_ANIM_CONTAINER);
_expandedPlayers.Add(player.userID);
PlaySound(player);
if (!_config.EnableAnimation)
{
ShowExpandedUI(player, 1f);
return;
}
float elapsedTime = 0f;
float stepInterval = 1f / ANIMATION_FPS;
float startTime = Time.realtimeSinceStartup;
bool done = false;
ShowExpandedUI(player, 0f);
_animationTimers[player.userID] = timer.Every(stepInterval, () =>
{
if (player == null || !player.IsConnected)
{
CleanupPlayer(player?.userID ?? 0);
return;
}
elapsedTime = Time.realtimeSinceStartup - startTime;
float t = Mathf.Clamp01(elapsedTime / ANIMATION_DURATION);
float easedT = EaseOutPower(t, EASING_POWER);
ShowExpandedUI(player, easedT);
if (!done && t >= 1f)
{
done = true;
StopAnimation(player.userID);
}
});
}
private void StartCollapseAnimation(BasePlayer player)
{
StopAnimation(player.userID);
PlaySound(player);
if (!_config.EnableAnimation)
{
_expandedPlayers.Remove(player.userID);
CuiHelper.DestroyUi(player, UI_ANIM_CONTAINER);
ShowLogoUI(player);
return;
}
float elapsedTime = 0f;
float stepInterval = 1f / ANIMATION_FPS;
float startTime = Time.realtimeSinceStartup;
bool done = false;
_animationTimers[player.userID] = timer.Every(stepInterval, () =>
{
if (player == null || !player.IsConnected)
{
CleanupPlayer(player?.userID ?? 0);
return;
}
elapsedTime = Time.realtimeSinceStartup - startTime;
float t = Mathf.Clamp01(elapsedTime / ANIMATION_DURATION);
float easedT = EaseOutPower(t, EASING_POWER);
ShowExpandedUI(player, 1f - easedT);
if (!done && t >= 1f)
{
done = true;
StopAnimation(player.userID);
_expandedPlayers.Remove(player.userID);
CuiHelper.DestroyUi(player, UI_ANIM_CONTAINER);
ShowLogoUI(player);
}
});
}
private void StopAnimation(ulong playerId)
{
if (_animationTimers.TryGetValue(playerId, out var t))
{
t?.Destroy();
_animationTimers.Remove(playerId);
}
}
private void CleanupPlayer(ulong playerId)
{
StopAnimation(playerId);
_expandedPlayers.Remove(playerId);
}
private void PlaySound(BasePlayer player)
{
var effect = new Effect(SOUND_EFFECT, player, 0, Vector3.zero, Vector3.zero);
EffectNetwork.Send(effect, player.Connection);
}
private float EaseOutPower(float t, float power)
{
return 1f - Mathf.Pow(1f - t, power);
}
private void DrawQR(CuiElementContainer container, bool[,] matrix, string parent, float offsetX = 0f, float offsetY = 0f)
{
int qrSize = matrix.GetLength(0);
int totalSize = qrSize + 2;
float moduleSize = 1f / totalSize;
for (int y = 0; y < qrSize; y++)
{
for (int x = 0; x < qrSize; x++)
{
if (matrix[x, y])
{
float xMin = moduleSize + (x * moduleSize) + offsetX;
float yMin = moduleSize + ((qrSize - 1 - y) * moduleSize) + offsetY;
container.Add(new CuiPanel
{
Image = { Color = _config.QrModuleColor },
RectTransform = {
AnchorMin = $"{xMin} {yMin}",
AnchorMax = $"{xMin + moduleSize} {yMin + moduleSize}"
}
}, parent);
}
}
}
}
private string EncodeUrl(string url)
{
if (string.IsNullOrEmpty(url)) return url;
var sb = new StringBuilder();
byte[] bytes = Encoding.UTF8.GetBytes(url);
foreach (byte b in bytes)
{
char c = (char)b;
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '-' || c == '_' ||
c == '.' || c == '~' || c == ':' || c == '/' ||
c == '?' || c == '&' || c == '=' || c == '#' ||
c == '%' || c == '+' || c == '@')
{
sb.Append(c);
}
else
{
sb.Append('%');
sb.Append(b.ToString("X2"));
}
}
return sb.ToString();
}
private static class QRCodeGen
{
public static int[] EXP = new int[256];
public static int[] LOG = new int[256];
static QRCodeGen()
{
int v = 1;
for (int i = 0; i < 256; i++)
{
EXP[i] = v;
LOG[v] = i;
v <<= 1;
if (v >= 256) v ^= 0x11D;
}
}
public static bool[,] Generate(string text)
{
byte[] bytes = Encoding.UTF8.GetBytes(text);
int ver = GetVersion(bytes.Length);
byte[] data = Encode(text);
int size = 17 + ver * 4;
var matrix = new bool[size, size];
var reserved = new bool[size, size];
AddFinders(matrix, reserved, size);
AddTiming(matrix, reserved, size);
int[] alignPos = GetAlignPositions(ver);
if (alignPos.Length > 0)
AddAlignments(matrix, reserved, alignPos, size);
if (ver >= 7)
ReserveVersionArea(reserved, size);
ReserveFormat(reserved, size);
PlaceData(matrix, reserved, data, size);
ApplyMask(matrix, reserved, size);
AddFormat(matrix, size);
if (ver >= 7)
AddVersionInfo(matrix, ver, size);
return matrix;
}
private static byte[] Encode(string text)
{
byte[] bytes = Encoding.UTF8.GetBytes(text);
int ver = GetVersion(bytes.Length);
int charCountBits = ver <= 9 ? 8 : 16;
var bits = new List<bool>();
AddBits(bits, 4, 4);
AddBits(bits, bytes.Length, charCountBits);
foreach (byte b in bytes) AddBits(bits, b, 8);
int cap = GetCapacity(ver);
int remaining = cap * 8 - bits.Count;
AddBits(bits, 0, Math.Min(4, remaining));
while (bits.Count % 8 != 0) bits.Add(false);
bool tog = true;
while (bits.Count < cap * 8)
{
AddBits(bits, tog ? 236 : 17, 8);
tog = !tog;
}
byte[] result = new byte[bits.Count / 8];
for (int i = 0; i < result.Length; i++)
for (int j = 0; j < 8; j++)
if (bits[i * 8 + j]) result[i] |= (byte)(128 >> j);
return AddEC(result, ver);
}
private static void AddBits(List<bool> bits, int val, int cnt)
{
for (int i = cnt - 1; i >= 0; i--)
bits.Add(((val >> i) & 1) == 1);
}
private static int GetVersion(int dataLen)
{
if (dataLen <= 17) return 1;
if (dataLen <= 32) return 2;
if (dataLen <= 53) return 3;
if (dataLen <= 78) return 4;
if (dataLen <= 106) return 5;
if (dataLen <= 134) return 6;
if (dataLen <= 154) return 7;
if (dataLen <= 192) return 8;
if (dataLen <= 230) return 9;
if (dataLen <= 271) return 10;
if (dataLen <= 321) return 11;
if (dataLen <= 367) return 12;
if (dataLen <= 425) return 13;
if (dataLen <= 458) return 14;
if (dataLen <= 520) return 15;
if (dataLen <= 586) return 16;
if (dataLen <= 644) return 17;
if (dataLen <= 718) return 18;
if (dataLen <= 792) return 19;
if (dataLen <= 858) return 20;
return 20;
}
private static int GetCapacity(int ver)
{
int[] caps = { 0, 19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647, 721, 795, 861 };
return ver < caps.Length ? caps[ver] : caps[caps.Length - 1];
}
private static int[] GetAlignPositions(int ver)
{
if (ver == 1) return new int[0];
int[][] positions = {
null,
new int[0],
new int[] { 6, 18 },
new int[] { 6, 22 },
new int[] { 6, 26 },
new int[] { 6, 30 },
new int[] { 6, 34 },
new int[] { 6, 22, 38 },
new int[] { 6, 24, 42 },
new int[] { 6, 26, 46 },
new int[] { 6, 28, 50 },
new int[] { 6, 30, 54 },
new int[] { 6, 32, 58 },
new int[] { 6, 34, 62 },
new int[] { 6, 26, 46, 66 },
new int[] { 6, 26, 48, 70 },
new int[] { 6, 26, 50, 74 },
new int[] { 6, 30, 54, 78 },
new int[] { 6, 30, 56, 82 },
new int[] { 6, 30, 58, 86 },
new int[] { 6, 34, 62, 90 },
};
return ver < positions.Length ? positions[ver] : positions[positions.Length - 1];
}
private static byte[] AddEC(byte[] data, int ver)
{
int cap = GetCapacity(ver);
byte[] padded = new byte[cap];
Array.Copy(data, padded, Math.Min(data.Length, cap));
int[] blockInfo = GetBlockInfo(ver);
int numBlocks1 = blockInfo[0];
int dataPerBlock1 = blockInfo[1];
int numBlocks2 = blockInfo[2];
int dataPerBlock2 = blockInfo[3];
int ecPerBlock = blockInfo[4];
var dataBlocks = new List<byte[]>();
var ecBlocks = new List<byte[]>();
int offset = 0;
for (int i = 0; i < numBlocks1; i++)
{
byte[] block = new byte[dataPerBlock1];
Array.Copy(padded, offset, block, 0, dataPerBlock1);
offset += dataPerBlock1;
dataBlocks.Add(block);
ecBlocks.Add(CalculateEC(block, ecPerBlock));
}
for (int i = 0; i < numBlocks2; i++)
{
byte[] block = new byte[dataPerBlock2];
Array.Copy(padded, offset, block, 0, dataPerBlock2);
offset += dataPerBlock2;
dataBlocks.Add(block);
ecBlocks.Add(CalculateEC(block, ecPerBlock));
}
var result = new List<byte>();
int maxDataLen = Math.Max(dataPerBlock1, dataPerBlock2);
for (int i = 0; i < maxDataLen; i++)
{
foreach (var block in dataBlocks)
{
if (i < block.Length)
result.Add(block[i]);
}
}
for (int i = 0; i < ecPerBlock; i++)
{
foreach (var block in ecBlocks)
{
result.Add(block[i]);
}
}
return result.ToArray();
}
private static int[] GetBlockInfo(int ver)
{
int[][] info = {
null,
new[] { 1, 19, 0, 0, 7 },
new[] { 1, 34, 0, 0, 10 },
new[] { 1, 55, 0, 0, 15 },
new[] { 1, 80, 0, 0, 20 },
new[] { 1, 108, 0, 0, 26 },
new[] { 2, 68, 0, 0, 18 },
new[] { 2, 78, 0, 0, 20 },
new[] { 2, 97, 0, 0, 24 },
new[] { 2, 116, 0, 0, 30 },
new[] { 2, 68, 2, 69, 18 },
new[] { 4, 81, 0, 0, 20 },
new[] { 2, 92, 2, 93, 24 },
new[] { 4, 107, 0, 0, 26 },
new[] { 3, 115, 1, 116, 30 },
new[] { 5, 87, 1, 88, 22 },
new[] { 5, 98, 1, 99, 24 },
new[] { 1, 107, 5, 108, 28 },
new[] { 5, 120, 1, 121, 30 },
new[] { 3, 113, 4, 114, 28 },
new[] { 3, 107, 5, 108, 28 },
};
return ver < info.Length ? info[ver] : info[info.Length - 1];
}
private static byte[] CalculateEC(byte[] data, int ecCount)
{
int[] gen = GetGenerator(ecCount);
int[] msg = new int[data.Length + ecCount];
for (int i = 0; i < data.Length; i++) msg[i] = data[i];
for (int i = 0; i < data.Length; i++)
{
int c = msg[i];
if (c != 0)
for (int j = 0; j < gen.Length; j++)
msg[i + j] ^= Mul(gen[j], c);
}
byte[] result = new byte[ecCount];
for (int i = 0; i < ecCount; i++)
result[i] = (byte)msg[data.Length + i];
return result;
}
private static int[] GetGenerator(int deg)
{
int[] r = { 1 };
for (int i = 0; i < deg; i++)
{
int[] t = new int[r.Length + 1];
for (int j = 0; j < r.Length; j++)
{
t[j] ^= r[j];
t[j + 1] ^= Mul(r[j], EXP[i]);
}
r = t;
}
return r;
}
private static int Mul(int a, int b)
{
if (a == 0 || b == 0) return 0;
return EXP[(LOG[a] + LOG[b]) % 255];
}
private static void AddFinders(bool[,] m, bool[,] r, int s)
{
foreach (int ox in new[] { 0, s - 7 })
{
foreach (int oy in new[] { 0, s - 7 })
{
if (ox != 0 && oy != 0) continue;
for (int dy = -1; dy <= 7; dy++)
{
for (int dx = -1; dx <= 7; dx++)
{
int x = ox + dx, y = oy + dy;
if (x < 0 || y < 0 || x >= s || y >= s) continue;
bool blk = dx >= 0 && dx <= 6 && dy >= 0 && dy <= 6 &&
(dx == 0 || dx == 6 || dy == 0 || dy == 6 ||
(dx >= 2 && dx <= 4 && dy >= 2 && dy <= 4));
m[x, y] = blk;
r[x, y] = true;
}
}
}
}
}
private static void AddTiming(bool[,] m, bool[,] r, int s)
{
for (int i = 8; i < s - 8; i++)
{
bool blk = i % 2 == 0;
m[i, 6] = blk; m[6, i] = blk;
r[i, 6] = true; r[6, i] = true;
}
}
private static void AddAlignments(bool[,] m, bool[,] r, int[] positions, int size)
{
if (positions.Length == 0) return;
foreach (int cy in positions)
{
foreach (int cx in positions)
{
if ((cx <= 8 && cy <= 8) ||
(cx <= 8 && cy >= size - 9) ||
(cx >= size - 9 && cy <= 8))
continue;
for (int dy = -2; dy <= 2; dy++)
{
for (int dx = -2; dx <= 2; dx++)
{
int x = cx + dx;
int y = cy + dy;
if (x < 0 || y < 0 || x >= size || y >= size) continue;
bool blk = dx == -2 || dx == 2 || dy == -2 || dy == 2 || (dx == 0 && dy == 0);
m[x, y] = blk;
r[x, y] = true;
}
}
}
}
}
private static void ReserveFormat(bool[,] r, int s)
{
for (int i = 0; i < 9; i++) { r[i, 8] = true; r[8, i] = true; }
for (int i = 0; i < 8; i++) { r[s - 1 - i, 8] = true; r[8, s - 1 - i] = true; }
r[8, s - 8] = true;
}
private static void ReserveVersionArea(bool[,] r, int s)
{
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 3; j++)
{
r[s - 11 + j, i] = true;
r[i, s - 11 + j] = true;
}
}
}
private static void AddVersionInfo(bool[,] m, int ver, int s)
{
int[] versionBits = {
0, 0, 0, 0, 0, 0, 0,
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D,
0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6
};
if (ver < 7 || ver >= versionBits.Length) return;
int bits = versionBits[ver];
for (int i = 0; i < 18; i++)
{
bool bit = ((bits >> i) & 1) == 1;
int row = i / 3;
int col = i % 3;
m[s - 11 + col, row] = bit;
m[row, s - 11 + col] = bit;
}
}
private static void PlaceData(bool[,] m, bool[,] r, byte[] d, int s)
{
int bi = 0, total = d.Length * 8;
bool upward = true;
for (int col = s - 1; col >= 1; col -= 2)
{
if (col == 6) col = 5;
for (int i = 0; i < s; i++)
{
int row = upward ? (s - 1 - i) : i;
for (int c = 0; c < 2; c++)
{
int x = col - c;
int y = row;
if (!r[x, y] && bi < total)
{
m[x, y] = ((d[bi / 8] >> (7 - bi % 8)) & 1) == 1;
bi++;
}
}
}
upward = !upward;
}
}
private static void ApplyMask(bool[,] m, bool[,] r, int s)
{
for (int y = 0; y < s; y++)
for (int x = 0; x < s; x++)
if (!r[x, y] && (x + y) % 2 == 0)
m[x, y] = !m[x, y];
}
private static void AddFormat(bool[,] m, int s)
{
int fmt = 0x77C4;
int[] hp = { 0, 1, 2, 3, 4, 5, 7, 8 };
int[] vp = { 8, 7, 5, 4, 3, 2, 1, 0 };
for (int i = 0; i < 8; i++)
{
bool b = ((fmt >> (14 - i)) & 1) == 1;
m[hp[i], 8] = b;
m[8, s - 1 - i] = b;
}
for (int i = 0; i < 7; i++)
{
bool b = ((fmt >> (6 - i)) & 1) == 1;
m[8, vp[i]] = b;
m[s - 8 + i, 8] = b;
}
m[8, s - 8] = true;
}
}
}
}