diff --git a/shadowsocks-csharp/3rd/QRCodeCS.cs b/shadowsocks-csharp/3rd/QRCodeCS.cs new file mode 100644 index 00000000..a380c978 --- /dev/null +++ b/shadowsocks-csharp/3rd/QRCodeCS.cs @@ -0,0 +1,1209 @@ +//--------------------------------------------------------------------- +// QRCode for C#4.0 Silverlight is translation of QRCode for JavaScript +// https://github.com/jeromeetienne/jquery-qrcode/ +// +// Copyright (c) 2009 Kazuhiko Arase +// +// URL: http://www.d-project.com/ +// +// Licensed under the MIT license: +// http://www.opensource.org/licenses/mit-license.php +// +// The word "QR Code" is registered trademark of +// DENSO WAVE INCORPORATED +// http://www.denso-wave.com/qrcode/faqpatent-e.html +// +//--------------------------------------------------------------------- +namespace QRCode4CS +{ + using System; + using System.Collections.Generic; + + + public class Error : Exception + { + public Error() { } + public Error(string message) : base(message) { } + public Error(string message, Exception inner) : base(message, inner) { } + } + + public enum QRMode : int + { + MODE_NUMBER = 1 << 0, + MODE_ALPHA_NUM = 1 << 1, + MODE_8BIT_BYTE = 1 << 2, + MODE_KANJI = 1 << 3 + } + + public enum QRErrorCorrectLevel : int + { + L = 1, + M = 0, + Q = 3, + H = 2 + } + + public enum QRMaskPattern : int + { + PATTERN000 = 0, + PATTERN001 = 1, + PATTERN010 = 2, + PATTERN011 = 3, + PATTERN100 = 4, + PATTERN101 = 5, + PATTERN110 = 6, + PATTERN111 = 7 + } + + public struct Options + { + public int Width { get; set; } + public int Height { get; set; } + public QRErrorCorrectLevel CorrectLevel { get; set; } + public int TypeNumber { get; set; } + public string Text { get; set; } + + public Options(string text) + : this() + { + Width = 256; + Height = 256; + TypeNumber = 4; + CorrectLevel = QRErrorCorrectLevel.H; + Text = text; + } + } + + internal static class QRUtil + { + internal const int G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); + internal const int G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); + internal const int G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); + internal static readonly int[][] PATTERN_POSITION_TABLE = new int[][] { + new int[] {}, + 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}, + new int [] {6, 28, 50, 72, 94}, + new int [] {6, 26, 50, 74, 98}, + new int [] {6, 30, 54, 78, 102}, + new int [] {6, 28, 54, 80, 106}, + new int [] {6, 32, 58, 84, 110}, + new int [] {6, 30, 58, 86, 114}, + new int [] {6, 34, 62, 90, 118}, + new int [] {6, 26, 50, 74, 98, 122}, + new int [] {6, 30, 54, 78, 102, 126}, + new int [] {6, 26, 52, 78, 104, 130}, + new int [] {6, 30, 56, 82, 108, 134}, + new int [] {6, 34, 60, 86, 112, 138}, + + new int [] {6, 30, 58, 86, 114, 142}, + new int [] {6, 34, 62, 90, 118, 146}, + new int [] {6, 30, 54, 78, 102, 126, 150}, + new int [] {6, 24, 50, 76, 102, 128, 154}, + new int [] {6, 28, 54, 80, 106, 132, 158}, + new int [] {6, 32, 58, 84, 110, 136, 162}, + new int [] {6, 26, 54, 82, 110, 138, 166}, + new int [] {6, 30, 58, 86, 114, 142, 170} + }; + internal static int GetLengthInBits(QRMode mode, int type) + { + + if (1 <= type && type < 10) + { + + // 1 - 9 + + switch (mode) + { + case QRMode.MODE_NUMBER: return 10; + case QRMode.MODE_ALPHA_NUM: return 9; + case QRMode.MODE_8BIT_BYTE: return 8; + case QRMode.MODE_KANJI: return 8; + default: + throw new Error("mode:" + mode); + } + + } + else if (type < 27) + { + + // 10 - 26 + + switch (mode) + { + case QRMode.MODE_NUMBER: return 12; + case QRMode.MODE_ALPHA_NUM: return 11; + case QRMode.MODE_8BIT_BYTE: return 16; + case QRMode.MODE_KANJI: return 10; + default: + throw new Error("mode:" + mode); + } + + } + else if (type < 41) + { + + // 27 - 40 + + switch (mode) + { + case QRMode.MODE_NUMBER: return 14; + case QRMode.MODE_ALPHA_NUM: return 13; + case QRMode.MODE_8BIT_BYTE: return 16; + case QRMode.MODE_KANJI: return 12; + default: + throw new Error("mode:" + mode); + } + + } + else + { + throw new Error("type:" + type); + } + } + + internal static double GetLostPoint(QRCode qrCode) + { + int moduleCount = qrCode.GetModuleCount(); + double lostPoint = 0; + for (int row = 0; row < moduleCount; row++) + { + + for (int col = 0; col < moduleCount; col++) + { + + var sameCount = 0; + var dark = qrCode.IsDark(row, col); + + for (var r = -1; r <= 1; r++) + { + + if (row + r < 0 || moduleCount <= row + r) + { + continue; + } + + for (var c = -1; c <= 1; c++) + { + + if (col + c < 0 || moduleCount <= col + c) + { + continue; + } + + if (r == 0 && c == 0) + { + continue; + } + + if (dark == qrCode.IsDark((int)((int)row + r), (int)((int)col + c))) + { + sameCount++; + } + } + } + + if (sameCount > 5) + { + lostPoint += (int)(3 + sameCount - 5); + } + } + } + + // LEVEL2 + + for (int row = 0; row < moduleCount - 1; row++) + { + for (int col = 0; col < moduleCount - 1; col++) + { + var count = 0; + if (qrCode.IsDark(row, col)) count++; + if (qrCode.IsDark(row + 1, col)) count++; + if (qrCode.IsDark(row, col + 1)) count++; + if (qrCode.IsDark(row + 1, col + 1)) count++; + if (count == 0 || count == 4) + { + lostPoint += 3; + } + } + } + + // LEVEL3 + + for (int row = 0; row < moduleCount; row++) + { + for (int col = 0; col < moduleCount - 6; col++) + { + if (qrCode.IsDark(row, col) + && !qrCode.IsDark(row, col + 1) + && qrCode.IsDark(row, col + 2) + && qrCode.IsDark(row, col + 3) + && qrCode.IsDark(row, col + 4) + && !qrCode.IsDark(row, col + 5) + && qrCode.IsDark(row, col + 6)) + { + lostPoint += 40; + } + } + } + + for (int col = 0; col < moduleCount; col++) + { + for (int row = 0; row < moduleCount - 6; row++) + { + if (qrCode.IsDark(row, col) + && !qrCode.IsDark(row + 1, col) + && qrCode.IsDark(row + 2, col) + && qrCode.IsDark(row + 3, col) + && qrCode.IsDark(row + 4, col) + && !qrCode.IsDark(row + 5, col) + && qrCode.IsDark(row + 6, col)) + { + lostPoint += 40; + } + } + } + + // LEVEL4 + + int darkCount = 0; + + for (int col = 0; col < moduleCount; col++) + { + for (int row = 0; row < moduleCount; row++) + { + if (qrCode.IsDark(row, col)) + { + darkCount++; + } + } + } + + double ratio = Math.Abs(100.0 * darkCount / moduleCount / moduleCount - 50) / 5; + lostPoint += ratio * 10; + + return lostPoint; + + } + + + + + internal static int GetBCHTypeInfo(int data) + { + int d = (data << 10); + int s = 0; + while ((s = (int)(QRUtil.GetBCHDigit(d) - QRUtil.GetBCHDigit(QRUtil.G15))) >= 0) + { + d ^= (Convert.ToInt32(QRUtil.G15) << s); + } + return (int)((data << 10) | d) ^ QRUtil.G15_MASK; + } + + internal static int GetBCHTypeNumber(int data) + { + int d = data << 12; + while (QRUtil.GetBCHDigit(d) - QRUtil.GetBCHDigit(QRUtil.G18) >= 0) + { + d ^= (QRUtil.G18 << (QRUtil.GetBCHDigit(d) - QRUtil.GetBCHDigit(QRUtil.G18))); + } + return (data << 12) | d; + } + + internal static int GetBCHDigit(int dataInt) + { + + int digit = 0; + uint data = Convert.ToUInt32(dataInt); + while (data != 0) + { + digit++; + data >>= 1; + } + + return digit; + } + + internal static int[] GetPatternPosition(int typeNumber) + { + return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; + } + + internal static bool GetMask(QRMaskPattern maskPattern, int i, int j) + { + + switch (maskPattern) + { + + case QRMaskPattern.PATTERN000: return (i + j) % 2 == 0; + case QRMaskPattern.PATTERN001: return i % 2 == 0; + case QRMaskPattern.PATTERN010: return j % 3 == 0; + case QRMaskPattern.PATTERN011: return (i + j) % 3 == 0; + case QRMaskPattern.PATTERN100: return (Math.Floor((double) (i / 2)) + Math.Floor((double) (j / 3))) % 2 == 0; + case QRMaskPattern.PATTERN101: return (i * j) % 2 + (i * j) % 3 == 0; + case QRMaskPattern.PATTERN110: return ((i * j) % 2 + (i * j) % 3) % 2 == 0; + case QRMaskPattern.PATTERN111: return ((i * j) % 3 + (i + j) % 2) % 2 == 0; + + default: + throw new Error("bad maskPattern:" + maskPattern); + } + } + + + + internal static QRPolynomial GetErrorCorrectPolynomial(int errorCorrectLength) + { + QRPolynomial a = new QRPolynomial(new DataCache() { 1 }, 0); + + for (int i = 0; i < errorCorrectLength; i++) + { + a = a.Multiply(new QRPolynomial(new DataCache() { 1, QRMath.GExp(i) }, 0)); + } + + return a; + } + } + + internal struct QRPolynomial + { + private int[] m_num; + public QRPolynomial(DataCache num, int shift) + : this() + { + if (num == null) + { + throw new Error(); + } + + int offset = 0; + + while (offset < num.Count && num[offset] == 0) + { + offset++; + } + + this.m_num = new int[num.Count - offset + shift]; + for (int i = 0; i < num.Count - offset; i++) + { + this.m_num[i] = num[(int)(i + offset)]; + } + } + + public int Get(int index) + { + return this.m_num[(int)index]; + } + + public int GetLength() + { + return (int)this.m_num.Length; + } + + public QRPolynomial Multiply(QRPolynomial e) + { + + var num = new DataCache(this.GetLength() + e.GetLength() - 1); + + for (int i = 0; i < this.GetLength(); i++) + { + for (int j = 0; j < e.GetLength(); j++) + { + num[i + j] ^= QRMath.GExp(QRMath.GLog(this.Get(i)) + QRMath.GLog(e.Get(j))); + } + } + + return new QRPolynomial(num, 0); + } + + public QRPolynomial Mod(QRPolynomial e) + { + + if (Convert.ToInt64(this.GetLength()) - Convert.ToInt64(e.GetLength()) < 0) + { + return this; + } + + int ratio = QRMath.GLog(this.Get(0)) - QRMath.GLog(e.Get(0)); + + var num = new DataCache(this.GetLength()); + + for (int i = 0; i < this.GetLength(); i++) + { + num[i] = this.Get(i); + } + + for (int i = 0; i < e.GetLength(); i++) + { + num[i] ^= QRMath.GExp(QRMath.GLog(e.Get(i)) + ratio); + } + + // recursive call + return new QRPolynomial(num, 0).Mod(e); + } + } + + + internal static class QRMath + { + private static readonly int[] EXP_TABLE; + private static readonly int[] LOG_TABLE; + + static QRMath() + { + EXP_TABLE = new int[256]; + LOG_TABLE = new int[256]; + for (int i = 0; i < 8; i++) + { + QRMath.EXP_TABLE[i] = (int)(1 << (int)i); + } + for (int i = 8; i < 256; i++) + { + QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] + ^ QRMath.EXP_TABLE[i - 5] + ^ QRMath.EXP_TABLE[i - 6] + ^ QRMath.EXP_TABLE[i - 8]; + } + for (int i = 0; i < 255; i++) + { + QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; + } + } + + internal static int GLog(int n) + { + if (n < 1) + { + throw new Error("glog(" + n + ")"); + } + return QRMath.LOG_TABLE[n]; + } + + internal static int GExp(int n) + { + while (n < 0) + { + n += 255; + } + while (n >= 256) + { + n -= 255; + } + return QRMath.EXP_TABLE[n]; + } + } + + public struct QR8bitByte + { + public QRMode Mode { get; private set; } + private string m_data { get; set; } + + public QR8bitByte(string data) + : this() + { + m_data = data; + Mode = QRMode.MODE_8BIT_BYTE; + } + + public int Length + { + get + { + return m_data.Length; + } + } + + public void Write(QRBitBuffer buffer) + { + for (int i = 0; i < m_data.Length; ++i) + { + //item + buffer.Put(m_data[i], 8); + } + ///buffer = Data; + } + + + } + + internal class DataCache : List + { + public DataCache(int capacity) + : base() + { + for (int i = 0; i < capacity; i++) + { + base.Add(0); + } + } + + public DataCache() + : base() + { + + } + + + } + + internal struct QRRSBlock + { + private static readonly int[][] RS_BLOCK_TABLE = new int[][] { + // L + // M + // Q + // H + + // 1 + new int [] {1, 26, 19}, + new int [] {1, 26, 16}, + new int [] {1, 26, 13}, + new int [] {1, 26, 9}, + + // 2 + new int [] {1, 44, 34}, + new int [] {1, 44, 28}, + new int [] {1, 44, 22}, + new int [] {1, 44, 16}, + + // 3 + new int [] {1, 70, 55}, + new int [] {1, 70, 44}, + new int [] {2, 35, 17}, + new int [] {2, 35, 13}, + + // 4 + new int [] {1, 100, 80}, + new int [] {2, 50, 32}, + new int [] {2, 50, 24}, + new int [] {4, 25, 9}, + + // 5 + new int [] {1, 134, 108}, + new int [] {2, 67, 43}, + new int [] {2, 33, 15, 2, 34, 16}, + new int [] {2, 33, 11, 2, 34, 12}, + + // 6 + new int [] {2, 86, 68}, + new int [] {4, 43, 27}, + new int [] {4, 43, 19}, + new int [] {4, 43, 15}, + + // 7 + new int [] {2, 98, 78}, + new int [] {4, 49, 31}, + new int [] {2, 32, 14, 4, 33, 15}, + new int [] {4, 39, 13, 1, 40, 14}, + + // 8 + new int [] {2, 121, 97}, + new int [] {2, 60, 38, 2, 61, 39}, + new int [] {4, 40, 18, 2, 41, 19}, + new int [] {4, 40, 14, 2, 41, 15}, + + // 9 + new int [] {2, 146, 116}, + new int [] {3, 58, 36, 2, 59, 37}, + new int [] {4, 36, 16, 4, 37, 17}, + new int [] {4, 36, 12, 4, 37, 13}, + + // 10 + new int [] {2, 86, 68, 2, 87, 69}, + new int [] {4, 69, 43, 1, 70, 44}, + new int [] {6, 43, 19, 2, 44, 20}, + new int [] {6, 43, 15, 2, 44, 16} + }; + + + public int DataCount { get; private set; } + public int TotalCount { get; set; } + + public QRRSBlock(int totalCount, int dataCount) + : this() + { + TotalCount = totalCount; + DataCount = dataCount; + } + public static List GetRSBlocks(int typeNumber, QRErrorCorrectLevel errorCorrectLevel) + { + + int[] rsBlock = GetRsBlockTable(typeNumber, errorCorrectLevel); + + if (rsBlock == null) + { + throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); + } + + int length = (int)rsBlock.Length / 3; + + var list = new List(); + + for (int i = 0; i < length; i++) + { + + int count = rsBlock[i * 3 + 0]; + int totalCount = rsBlock[i * 3 + 1]; + int dataCount = rsBlock[i * 3 + 2]; + + for (int j = 0; j < count; j++) + { + list.Add(new QRRSBlock(totalCount, dataCount)); + } + } + + return list; + } + + private static int[] GetRsBlockTable(int typeNumber, QRErrorCorrectLevel errorCorrectLevel) + { + switch (errorCorrectLevel) + { + case QRErrorCorrectLevel.L: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; + case QRErrorCorrectLevel.M: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; + case QRErrorCorrectLevel.Q: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; + case QRErrorCorrectLevel.H: + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; + default: + return null; + } + } + } + + public class QRCode + { + private const int PAD0 = 0xEC; + private const int PAD1 = 0x11; + private List m_dataList = new List(); + private int m_typeNumber; + private DataCache m_dataCache; + private int m_moduleCount; + private bool?[][] m_modules; + private QRErrorCorrectLevel m_errorCorrectLevel; + public QRCode(Options options) + : this(options.TypeNumber, options.CorrectLevel) + { + AddData(options.Text); + } + + public QRCode(int typeNumber, QRErrorCorrectLevel level) + { + m_typeNumber = typeNumber; + m_errorCorrectLevel = level; + m_dataCache = null; + } + + public void AddData(string data) + { + m_dataCache = null; + m_dataList.Add(new QR8bitByte(data)); + } + + public void Make() + { + MakeImpl(false, GetBestMaskPattern()); + } + + private QRMaskPattern GetBestMaskPattern() + { + double minLostPoint = 0; + QRMaskPattern pattern = 0; + for (int i = 0; i < 8; i++) + { + this.MakeImpl(true, (QRMaskPattern)i); + + double lostPoint = QRUtil.GetLostPoint(this); + + if (i == 0 || minLostPoint > lostPoint) + { + minLostPoint = lostPoint; + pattern = (QRMaskPattern)i; + } + } + + return pattern; + } + + + + private void MakeImpl(bool test, QRMaskPattern maskPattern) + { + m_moduleCount = this.m_typeNumber * 4 + 17; + m_modules = new bool?[m_moduleCount][]; + for (int row = 0; row < m_moduleCount; row++) + { + + m_modules[row] = new bool?[(m_moduleCount)]; + + for (var col = 0; col < m_moduleCount; col++) + { + m_modules[row][col] = null; //(col + row) % 3; + } + } + + this.SetupPositionProbePattern(0, 0); + this.SetupPositionProbePattern(m_moduleCount - 7, 0); + this.SetupPositionProbePattern(0, m_moduleCount - 7); + this.SetupPositionAdjustPattern(); + this.SetupTimingPattern(); + this.setupTypeInfo(test, maskPattern); + + if (m_typeNumber >= 7) + { + this.setupTypeNumber(test); + } + + if (this.m_dataCache == null) + { + this.m_dataCache = CreateData(this.m_typeNumber, this.m_errorCorrectLevel, this.m_dataList); + } + + MapData(this.m_dataCache, maskPattern); + + } + + public bool IsDark(int row, int col) + { + return m_modules[(int)row][(int)col].Value; + } + + private void SetupTimingPattern() + { + for (var r = 8; r < this.m_moduleCount - 8; r++) + { + if (this.m_modules[r][6] != null) + { + continue; + } + this.m_modules[r][6] = (r % 2 == 0); + } + + for (var c = 8; c < this.m_moduleCount - 8; c++) + { + if (this.m_modules[6][c] != null) + { + continue; + } + this.m_modules[6][c] = (c % 2 == 0); + } + } + + private void setupTypeNumber(bool test) + { + var bits = QRUtil.GetBCHTypeNumber(m_typeNumber); + + for (var i = 0; i < 18; i++) + { + var mod = (!test && ((bits >> i) & 1) == 1); + this.m_modules[(int)Math.Floor((double) (i / 3))][i % 3 + this.m_moduleCount - 8 - 3] = mod; + } + + for (var i = 0; i < 18; i++) + { + var mod = (!test && ((bits >> i) & 1) == 1); + this.m_modules[i % 3 + this.m_moduleCount - 8 - 3][(int)Math.Floor((double) (i / 3))] = mod; + } + } + private void SetupPositionAdjustPattern() + { + var pos = QRUtil.GetPatternPosition(m_typeNumber); + + for (var i = 0; i < pos.Length; i++) + { + + for (var j = 0; j < pos.Length; j++) + { + + var row = pos[i]; + var col = pos[j]; + + if (this.m_modules[row][col] != null) + { + continue; + } + + for (var r = -2; r <= 2; r++) + { + + for (var c = -2; c <= 2; c++) + { + + if (r == -2 || r == 2 || c == -2 || c == 2 + || (r == 0 && c == 0)) + { + this.m_modules[row + r][col + c] = true; + } + else + { + this.m_modules[row + r][col + c] = false; + } + } + } + } + } + } + + + private void setupTypeInfo(bool test, QRMaskPattern maskPattern) + { + + var data = ((int)this.m_errorCorrectLevel << 3) | (int)maskPattern; + var bits = QRUtil.GetBCHTypeInfo(data); + + // vertical + for (var i = 0; i < 15; i++) + { + + var mod = (!test && ((bits >> i) & 1) == 1); + + if (i < 6) + { + this.m_modules[i][8] = mod; + } + else if (i < 8) + { + this.m_modules[i + 1][8] = mod; + } + else + { + this.m_modules[this.m_moduleCount - 15 + i][8] = mod; + } + } + + // horizontal + for (var i = 0; i < 15; i++) + { + + var mod = (!test && ((bits >> i) & 1) == 1); + + if (i < 8) + { + this.m_modules[8][this.m_moduleCount - i - 1] = mod; + } + else if (i < 9) + { + this.m_modules[8][15 - i - 1 + 1] = mod; + } + else + { + this.m_modules[8][15 - i - 1] = mod; + } + } + + // fixed module + this.m_modules[this.m_moduleCount - 8][8] = (!test); + + } + + private void MapData(DataCache data, QRMaskPattern maskPattern) + { + + int inc = -1; + int row = (int)this.m_moduleCount - 1; + int bitIndex = 7; + int byteIndex = 0; + + for (var col = this.m_moduleCount - 1; col > 0; col -= 2) + { + + if (col == 6) col--; + + while (true) + { + + for (int c = 0; c < 2; c++) + { + + if (this.m_modules[row][col - c] == null) + { + + bool dark = false; + + if (byteIndex < data.Count) + { + dark = (((Convert.ToUInt32(data[byteIndex]) >> bitIndex) & 1) == 1); + } + + bool mask = QRUtil.GetMask(maskPattern, (int)row, col - c); + + if (mask) + { + dark = !dark; + } + + this.m_modules[row][col - c] = dark; + bitIndex--; + + if (bitIndex == -1) + { + byteIndex++; + bitIndex = 7; + } + } + } + + row += inc; + + if (row < 0 || this.m_moduleCount <= row) + { + row -= inc; + inc = -inc; + break; + } + } + } + + } + + private DataCache CreateData(int typeNumber, QRErrorCorrectLevel errorCorrectLevel, List dataList) + { + List rsBlocks = QRRSBlock.GetRSBlocks(typeNumber, errorCorrectLevel); + + var buffer = new QRBitBuffer(); + + for (int i = 0; i < dataList.Count; i++) + { + QR8bitByte data = dataList[i]; + + buffer.Put((int)data.Mode, 4); + buffer.Put(data.Length, QRUtil.GetLengthInBits(data.Mode, typeNumber)); + data.Write(buffer); + } + + // calc num max data. + int totalDataCount = 0; + for (var i = 0; i < rsBlocks.Count; i++) + { + totalDataCount += rsBlocks[i].DataCount; + } + + if (buffer.GetLengthInBits() > totalDataCount * 8) + { + throw new Error("code length overflow. (" + + buffer.GetLengthInBits() + + ">" + + totalDataCount * 8 + + ")"); + } + + // end code + if (buffer.GetLengthInBits() + 4 <= totalDataCount * 8) + { + buffer.Put(0, 4); + } + + // padding + while (buffer.GetLengthInBits() % 8 != 0) + { + buffer.PutBit(false); + } + + // padding + while (true) + { + + if (buffer.GetLengthInBits() >= totalDataCount * 8) + { + break; + } + buffer.Put(QRCode.PAD0, 8); + + if (buffer.GetLengthInBits() >= totalDataCount * 8) + { + break; + } + buffer.Put(QRCode.PAD1, 8); + } + + return CreateBytes(buffer, rsBlocks); + } + + private DataCache CreateBytes(QRBitBuffer buffer, List rsBlocks) + { + + int offset = 0; + + int maxDcCount = 0; + int maxEcCount = 0; + + var dcdata = new DataCache[(rsBlocks.Count)]; + var ecdata = new DataCache[(rsBlocks.Count)]; + + for (int r = 0; r < rsBlocks.Count; r++) + { + + int dcCount = rsBlocks[(int)r].DataCount; + int ecCount = rsBlocks[(int)r].TotalCount - dcCount; + + maxDcCount = Math.Max(maxDcCount, dcCount); + maxEcCount = Math.Max(maxEcCount, ecCount); + + dcdata[r] = new DataCache(dcCount); + + for (int i = 0; i < dcdata[r].Count; i++) + { + dcdata[r][i] = 0xff & buffer.m_buffer[(int)(i + offset)]; + } + offset += dcCount; + + QRPolynomial rsPoly = QRUtil.GetErrorCorrectPolynomial(ecCount); + QRPolynomial rawPoly = new QRPolynomial(dcdata[r], rsPoly.GetLength() - 1); + + var modPoly = rawPoly.Mod(rsPoly); + ecdata[r] = new DataCache(rsPoly.GetLength() - 1); + for (int i = 0; i < ecdata[r].Count; i++) + { + int modIndex = i + modPoly.GetLength() - (int)ecdata[r].Count; + ecdata[r][i] = (modIndex >= 0) ? modPoly.Get(modIndex) : 0; + } + + } + + int totalCodeCount = 0; + for (int i = 0; i < rsBlocks.Count; i++) + { + totalCodeCount += rsBlocks[(int)i].TotalCount; + } + + var data = new DataCache(totalCodeCount); + int index = 0; + + for (int i = 0; i < maxDcCount; i++) + { + for (int r = 0; r < rsBlocks.Count; r++) + { + if (i < dcdata[r].Count) + { + data[index++] = dcdata[r][i]; + } + } + } + + for (int i = 0; i < maxEcCount; i++) + { + for (int r = 0; r < rsBlocks.Count; r++) + { + if (i < ecdata[r].Count) + { + data[index++] = ecdata[r][i]; + } + } + } + + return data; + + } + private void SetupPositionProbePattern(int row, int col) + { + for (int r = -1; r <= 7; r++) + { + + if (row + r <= -1 || this.m_moduleCount <= row + r) continue; + + for (int c = -1; c <= 7; c++) + { + + if (col + c <= -1 || this.m_moduleCount <= col + c) continue; + + if ((0 <= r && r <= 6 && (c == 0 || c == 6)) + || (0 <= c && c <= 6 && (r == 0 || r == 6)) + || (2 <= r && r <= 4 && 2 <= c && c <= 4)) + { + this.m_modules[row + r][col + c] = true; + } + else + { + this.m_modules[row + r][col + c] = false; + } + } + } + } + + public int GetModuleCount() + { + return this.m_moduleCount; + } + + internal int getBestMaskPattern() + { + + double minLostPoint = 0; + int pattern = 0; + + for (int i = 0; i < 8; i++) + { + + this.MakeImpl(true, (QRMaskPattern)i); + + double lostPoint = QRUtil.GetLostPoint(this); + + if (i == 0 || minLostPoint > lostPoint) + { + minLostPoint = lostPoint; + pattern = i; + } + } + + return pattern; + } + } + + public class QRBitBuffer + { + internal List m_buffer = new List(); + private int m_length = 0; + public bool Get(int index) + { + int bufIndex = Convert.ToInt32(Math.Floor((double) (index / 8))); + return ((Convert.ToUInt32(this.m_buffer[bufIndex]) >> (7 - index % 8)) & 1) == 1; + } + + public void Put(int num, int length) + { + for (var i = 0; i < length; i++) + { + this.PutBit(((Convert.ToUInt32(num) >> (length - i - 1)) & 1) == 1); + } + } + + public int GetLengthInBits() + { + return m_length; + } + + public void PutBit(bool bit) + { + + int bufIndex = (int)Math.Floor((double) (this.m_length / 8)); + if (this.m_buffer.Count <= bufIndex) + { + this.m_buffer.Add(0); + } + + if (bit) + { + this.m_buffer[bufIndex] |= (int)(Convert.ToUInt32(0x80) >> (this.m_length % 8)); + } + this.m_length++; + } + } +} diff --git a/shadowsocks-csharp/View/QRCodeForm.Designer.cs b/shadowsocks-csharp/View/QRCodeForm.Designer.cs index 0a934d71..7162c70b 100755 --- a/shadowsocks-csharp/View/QRCodeForm.Designer.cs +++ b/shadowsocks-csharp/View/QRCodeForm.Designer.cs @@ -28,7 +28,10 @@ /// private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(QRCodeForm)); this.QRCodeWebBrowser = new System.Windows.Forms.WebBrowser(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // QRCodeWebBrowser @@ -42,22 +45,34 @@ this.QRCodeWebBrowser.Name = "QRCodeWebBrowser"; this.QRCodeWebBrowser.ScriptErrorsSuppressed = true; this.QRCodeWebBrowser.ScrollBarsEnabled = false; - this.QRCodeWebBrowser.Size = new System.Drawing.Size(200, 200); + this.QRCodeWebBrowser.Size = new System.Drawing.Size(201, 203); this.QRCodeWebBrowser.TabIndex = 0; // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(0, 0); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(205, 205); + this.pictureBox1.TabIndex = 1; + this.pictureBox1.TabStop = false; + // // QRCodeForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.ClientSize = new System.Drawing.Size(204, 202); + this.ClientSize = new System.Drawing.Size(205, 205); + this.Controls.Add(this.pictureBox1); this.Controls.Add(this.QRCodeWebBrowser); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.Name = "QRCodeForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "QRCode"; this.Load += new System.EventHandler(this.QRCodeForm_Load); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); } @@ -65,5 +80,6 @@ #endregion private System.Windows.Forms.WebBrowser QRCodeWebBrowser; + private System.Windows.Forms.PictureBox pictureBox1; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/QRCodeForm.cs b/shadowsocks-csharp/View/QRCodeForm.cs index 36c2c804..f34d8101 100755 --- a/shadowsocks-csharp/View/QRCodeForm.cs +++ b/shadowsocks-csharp/View/QRCodeForm.cs @@ -1,4 +1,5 @@ -using Shadowsocks.Properties; +using QRCode4CS; +using Shadowsocks.Properties; using System; using System.Collections.Generic; using System.ComponentModel; @@ -21,6 +22,46 @@ namespace Shadowsocks.View InitializeComponent(); } + private void GenQR(string ssconfig) + { + string qrText = ssconfig; + QRCode4CS.QRCode qrCoded = new QRCode4CS.QRCode(6, QRErrorCorrectLevel.M); + qrCoded.AddData(qrText); + qrCoded.Make(); + int blockSize = 5; + Bitmap drawArea = new Bitmap((qrCoded.GetModuleCount() * blockSize), (qrCoded.GetModuleCount() * blockSize)); + for (int row = 0; row < qrCoded.GetModuleCount(); row++) + { + for (int col = 0; col < qrCoded.GetModuleCount(); col++) + { + bool isDark = qrCoded.IsDark(row, col); + if (isDark) + { + for (int y = 0; y < blockSize; y++) + { + int myCol = (blockSize * (col - 1)) + (y + blockSize); + for (int x = 0; x < blockSize; x++) + { + drawArea.SetPixel((blockSize * (row - 1)) + (x + blockSize), myCol, Color.Black); + } + } + } + else + { + for (int y = 0; y < blockSize; y++) + { + int myCol = (blockSize * (col - 1)) + (y + blockSize); + for (int x = 0; x < blockSize; x++) + { + drawArea.SetPixel((blockSize * (row - 1)) + (x + blockSize), myCol, Color.White); + } + } + } + } + } + pictureBox1.Image = drawArea; + } + private string QRCodeHTML(string ssURL) { string html = Resources.qrcode; @@ -46,7 +87,8 @@ namespace Shadowsocks.View private void QRCodeForm_Load(object sender, EventArgs e) { - QRCodeWebBrowser.DocumentText = QRCodeHTML(code); + //QRCodeWebBrowser.DocumentText = QRCodeHTML(code); + GenQR(code); } } } diff --git a/shadowsocks-csharp/View/QRCodeForm.resx b/shadowsocks-csharp/View/QRCodeForm.resx index 5ea0895e..a7094c64 100755 --- a/shadowsocks-csharp/View/QRCodeForm.resx +++ b/shadowsocks-csharp/View/QRCodeForm.resx @@ -117,4 +117,333 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + AAABAAQAEBAAAAEAIABoBAAARgAAACAgAAABACAAqBAAAK4EAAAwMAAAAQAgAKglAABWFQAAAAAAAAEA + IADNEAAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA1qtqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKDJIvXrm8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAfxy/w4YpewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwoMjuMOFJ6Po0a4DAAAAAM+dUxPFijBf3bmEBQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODBkQbMl0ckw4UocsGCIsDAfxz8v30Z/8eN + NTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADTpmEHzJdHKwAAAADFiS+Fv30a/799Gf+/fRn/v30Z/799 + Gf/Dhil3AAAAAAAAAAAAAAAA4cKUAseNNUPChCWdwIAe8MB+G/zFiS5v0KBWC8GCIc6/fRn/v30Z/799 + Gf+/fRn/woIjswAAAAAAAAAAzZlLGcKEJcy/fRn/v30Z/799Gf+/fRn/v30Z/8SHK5fOm04iwYAf5799 + Gf+/fRn/v30Z/8CAHu7r17cCAAAAAAAAAADXrW4MxYoweMGBIOu/fRn/v30Z/799Gf+/fRn/woQlusqT + QUjAfxz2v30Z/799Gf+/fRn/ypNAMAAAAAAAAAAAAAAAAAAAAADXrW4MxYkueMGAH+u/fRn/v30Z/799 + Gf/BgSHWx4w0e79+Gv6/fRn/v30Z/8OFJ2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADTpmEMxIgseMGA + H+u/fRn/v30Z/8CAHuvFii++v30Z/799Gf/CgySqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADPnlMMw4YoeMCAHuu/fRn/wH8d+sKDI/y/fRn/wYEg5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADPnlMMw4UneMB/Heu/fRn/v30Z/799Gf/Ml0YmAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLlUQMwoMkeMB/Heu/fRn/xIcrZAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSpF4PxYkvf8WJL5AAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmzKQD/v8AAP5/ + AAD+fwAA/iMAAP4DAADyAwAAgAMAAAABAACAAQAA4AEAAPgBAAD+AQAA/4AAAP/gAAD/+AAA//4AACgA + AAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1qtqMAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAfx1/zJdHTAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL99 + GX/BgiLj165vEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + Gf+/fRn/v30Z/799Gf/BgSH7y5ZFYM+eUyjCgyTzv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/w4UotwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANOmYTDGjDOvwIAe/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/Afhv/yZE9h82ZS0jAfx37v30Z/799Gf+/fRn/v30Z/799 + Gf/CgyPz9+/iBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTpmEwxowzr8CAHv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/x401r82aTHy/fRr/v30Z/799 + Gf+/fRn/v30Z/799Gf/So104AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA06ZhMMaMM6/Afx3/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/xIYqy8qT + QLO/fRn/v30Z/799Gf+/fRn/v30Z/8eON3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM+eUzDFiS6vwH8d/799Gf+/fRn/v30Z/799 + Gf+/fRn/xIcq68iPOvO/fRn/v30Z/799Gf+/fRn/xIcqrwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPnlMwxYkur8B/ + Hf+/fRn/v30Z/799Gf+/fRn/wYEh/799Gf+/fRn/v30Z/799Gf/DhSjvAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAz55TMMWJLq/Afx3/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/XrW4wAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMuVRDDDhimvwH8c/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8eM + NGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLlUQww4Ypr8B/HP+/fRn/v30Z/799 + Gf+/fRn/xYgtpwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy5VEMMOG + Ka/Afxz/v30Z/799Gf/EhirnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAANKkXjzHjTa/wH4b/799Gv/ZsXYkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVqWdAx402v8mSPmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5sykDAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////9 + /////P////x////8f////D////wf///8PB///eAf//8AH//8AB//jAAP/AYAD+ADAA+AAIAPgABAB+AA + AAf4AAAH/gAAB/+AAAP/4AAD//gAA//+AAP//4AD///gAf//+AH///4B////gf///+D////4/////v// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1qxtSNSoZiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv30ZQMeMNMPp07AEAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv30ZQL99 + Gv/MmElwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAv30ZQL99Gf/BgiHz165vJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAv30ZQL99Gf+/fRn/x4w0w9++jAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyJA7QL99Gf+/fRn/v34a/82ZSnEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy5ZFQL99Gf+/fRn/v30Z/8KD + I/TXrnAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnzqgDzJdHT8SHK8+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/GizLb2LBzJAAA + AAAAAAAA2K9yQ8KDI/m/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf/BgiL/69e3EgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPv38QbVqWZmyI844b9+ + Gv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/xIcr8NWqaEQAAAAAAAAAANOlYWbAgB7+v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/z55TTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA9OnYCNSpZmjIjzjfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8KDJPvPnVJiAAAAAAAAAADPnVKPv30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/ypNAjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt278G1atqasiOOOG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/AgB7/zZpNifv38Qbz59QDyZA7rr99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/yI850QAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPv38QbVqWZmyI844b9+ + Gv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30a/8qU + QrHcuIEK6NCsD8WILcq/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/woIj9uTH + nA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA9OnYCNSnY2jGizLfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf/IjzjS2rR7G969iiXChCXiv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/8+dUkEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnz6kG0qRdasaLMuG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/xoox6dCgVy3ZsnY/wYIh9r99Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8qUQoUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPfv4gbRoVlmxowz4b99 + Gv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8OGKPnQoFdS0qReYMB/ + Hf2/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8eNNsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA79/GCNGiWmjGizLfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf/BgiH/zpxQeM6cUIi/fhr/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8OGKfXlyqILAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnz6kG0qRdasaLMuG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v34b/syYSKPLlkW0v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf/WrGw9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO/exQbNmkxmxYku4b99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/LlUTSyZI95L99Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf/LlUN8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA586pCM2aTWjFiC3fv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/yZI+/8SH + K/+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/GjDO3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADfvowGzpxQasWILeG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/79+G/+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/EiCzzAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO/exQbNmkxmxYku4b99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/376NPAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA586pCM2ZS2jDhSjfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/ypRBbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADXrm8Gy5VDasOFJ+G/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/xosxqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOfOqAbJkj5mw4Yp4b99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/xIcq5/Pn1AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA376MCMqTQGjDhSjfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/wIAd/9y5gy8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADXrm8Gy5VDasOFJ+G/fRn/v30Z/799Gf+/fRn/v30Z/86b + T2sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN++jAbSo1yAxYov7b99 + Gf+/fRn/v30Z/8iPOaYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA69W2EdGhWX/GijHuv30Z/8SHKt3v3sUCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt278S0KBXf8eON/DduoUiAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDgyQ/ZsncMAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////AAD///////8AAP//+f///wAA///4////AAD///j///8AAP// + +H///wAA///4P///AAD///g///8AAP//+B///wAA///4D///AAD///gP+f8AAP//+D/A/wAA///5/AD/ + AAD////gAP8AAP///wAA/wAA///4AAB/AAD///gAAH8AAP/8PAAAfwAA/8AeAAA/AAD/AA8AAD8AAPgA + BwAAPwAAwAABgAA/AACAAADAAD8AAMAAAGAAHwAA8AAAMAAfAAD8AAAYAB8AAP8AAAAAHwAA/8AAAAAP + AAD/8AAAAA8AAP/8AAAADwAA//8AAAAPAAD//8AAAAcAAP//8AAABwAA///8AAAHAAD///8AAAcAAP// + /8AABwAA////8AADAAD////8AAMAAP////8AAwAA/////8ABAAD/////8AEAAP/////8AQAA//////8B + AAD//////8AAAP//////8AAA///////8AAD///////8AAP///////wAAiVBORw0KGgoAAAANSUhEUgAA + AQAAAAEACAYAAABccqhmAAAQlElEQVR42u3dP0xcZ7rH8R8riikQoqCYlWzJlijMhcJIWwQrxbqIFkrH + 2Vh2t1bcbBXWXRqL7Z11lcYR6TbC+4cSoi24FdyVIkERBIUlkEBXUyDdEaKYAuncwn69h+M5M+fM+ff+ + +X66WEQ54/D8zvM+553zjkVRJABh+lXTFwCgOQQAEDACAAgYAQAEjAAAAkYAAAEjAICAEQBAwAgAIGAE + ABAwAgAIGAEABIwAAAJGAAABIwAAz130rqKX2yfRysbRR9/9H2/64gBU46J3Fb3ePdPr3TNd9K60ujzz + 0c8QAIBnkoVv3Ls99dHPEgCAJ9IKX5ImW+Oaa0+MJf8dAgBw3KDCN/rd/SUCAHBWlsI3Fm9N9f1zAgBw + TJ7CN+baE33/nAAAHDFK4Rv3bk+N9ftzAgCwXJHCl9LX/xIBAFiraOEbaet/iQAArFNW4RsEAOCAsgvf + SFv/SwQA0LiqCl8avP6XCACgMVUWvjGo/ZcIAKB2dRS+kfb83yAAgJrUWfgGSwDAAi+3T6I6C196d/ef + bI2PDfoZAgCo0PpeJ/p2+0Sn3V7t/+1hd3+JAAAq0WThG8MGgBIBAJTKhsI36ACAmthU+JJ0c6o1dP0v + EQBAIbYVvpHl7i8RAMBIbC1845MM63+JAABysb3wDToAoESuFL70bv1/c6o1dP0vEQDAQC4VvpH17i8R + AEBfLha+kXX9LxEAwDUuF74x/+uJzD9LAADyo/Cl9ANA0hAACJovhW/kWf9LBAAC5VvhG1n2/8cRAAiK + r4Vv0AEAffhe+FL+9b9EAMBzIRS+kffuLxEA8FRIhW8Me/9fPwQAvBJi4Rt5B4ASAQBPhFz4xqADQNIQ + AHAahf/OKOt/iQCAoyj860Zp/yUCAI6h8PsjAOA1Cn+wUdb/EgEAy+0cd6Nv//tEO8fdpi/FWqM8/jMI + AFiJws9u1AGgRADAMhR+fqOu/yUCAJag8EdHBwBnUfjFZDkAdBACAI2g8MtR5O4vEQCoGYVfriLrf4kA + QE0o/GrM5XgBaD8EACpF4VcnzwEgaQgAVILCr17R9b9EAKBkFH598hwAkoYAQCko/PrRAaBxvhf+ZGtc + F72rpi/jI2Ws/yUCACPyvfAlaWl22trPV3T6bxAAyCWEwpek1eUZvdnrWHn3l4o//zcIAGQSSuFPtsb1 + 6sEd/XR0roPOZdOXk6qM9b9EAGCIUApfereuXnsyr63Dc63vdZq+nFSjHACShgBAXyEVvvTuSzX/eHpX + m4fnerl90vTlDFTW3V8iAJAQWuFL0qOFtlaXZ3Ta7Wll46jpyxmqrPW/RADgvRALX5Ke37+l5/dvjZ12 + e9HDtf2mLycTOgCUJtTCl6S/PLijRwvtsYveVfT0r79YO/FPKmv9LxEAwQq58Cdb4/rH07sfCunrjSOr + J/5xZd79JQIgOCEXvvRu2Lf2ZP7DLroXm2+jrcPzpi8rszLX/xIBEIzQC196d/dcezz/4RVa63ud6PXu + WdOXlQsBgFxOu73oxeZbuXSXq8Kjhbb+8uDOh7XzQecycmHinzTqASBpCABPnXZ70bfbJ1ZvaKmLGfaZ + f3Zp4h9X9vpfIgC8Q+H/h9nWuzQ7/aH4XZv4x5Xd/ksEgDco/OvMtt7kIzOXJv5JBAA+QuF/zGzrTb4v + 37WJf9J8SV8BjiMAHEXh95cc9hkuTvzjih4AkoYAcAyFn85s603+uasT/7gqBoASAeAMCj/dZGtcq8sz + 1yb9hqsT/6Qq1v8SAWA9Cn+w5LbeOJcn/kl0AIGh8IdLbutNevrjL85O/JOfs4r1v0QAWIfCz2Zpdlqv + HtxJLYyVjaPIl23PVUz/DQLAEhR+dmmTfmN9rxP59PdYxgEgaQiAhlH4+SS39SbtHHedn/gnVbX+lwiA + xlD4+Uy2xrX2eH7gl2EOOpfR0x9/afpSS1XWASBpCICaUfj5pW3rjbvoXUVf//PIi4l/XJV3f4kAqA2F + P5q0bb1Jvkz8k6pc/0sEQOUo/NENG/YZPk38k+gAHEXhF7O6PKNnizeGFr9vE/+4ydZ4pet/iQAoHYVf + zKBtvUk+Tvzjqr77SwRAaSj84gZt603yceKfVNX+/zgCoCAKvxxZh32SvxP/JDoAi1H45Rm2rTfJ14l/ + XJkHgA5CAORE4Zfr2eINrS7PZP5F93niH1fH3V8iADKj8Ms3bFtv0uvdM28n/kl1rP8lAmCoi95V9HL7 + RC6/Tso2Wbb1Jm0dnkcvNt82fem1mWtX9w3AOAIgxUXvKnq9e6bXu2feD5vqNNee0KvP7+Ra3x50LqOv + PX7c10/ZB4CkIQASKPzq5Jn0Gz691Serutb/EgHwAYVfrazbepMeru3rtNtr+vJrVdf6XyIAKPwaZN3W + m7SycRT5/rivHwKgBhR+9fodzZVVSBP/pLrW/1KAAUDh1yPPtt6k0Cb+cXWu/6WAAoDCr88owz4jxIl/ + XF2P/wzvA4DCr9ejhbZWl2dGKv4QJ/5Jda7/JY8DgMKvX95tvUkhTvyTWAIUROE3I++23qRQJ/5xVR4A + ksabAKDwm1Fk2GeEPPGPq/vuL3kQABR+c0bZ1psU8sQ/qe71v+RwAFD4zbp3e0prj+cLtayhT/yT6AAy + oPCbN+q23riL3lX0cG2f/4fv3Zxq1b7+lxwKAArfDqNu642j+D/WxN1fciAAKHw7FNnWm/Ri8633r/TK + q+oDQNJYGwAUvj3KmPQbL7dPmPj3QQfwHoVvlyLbepPW9zrRy+2Tpj+Sdao+AHQQawKAwrdPkW29SQed + Sx73pWjq7i9ZEAAUvp2e37+l5/dvlXJXYug32H/V/AWguMYCgMK3V9FtvXEU/3BBdQAUvr3KHPYZTPwH + q+sAkDS1BQCFb7e59oTWnsyXOoxi4j9ck3d/qaYAeLl9ElH49ipjW28SE/9smtj/H1dpAKzvdaJvt0+C + /463zcrY1pvExD87LzsACt8NZQ77DIZ+2TW9/pdKDgAK3w1lbuuNo/jzmf91c4//jFICgMJ3x82pltae + zFdy52Hin0/T63+pYABQ+G4pc1tv0ovNt0z8c3I2ACh895S5rTdpfa8TcXpyfnUeAJImVwBQ+G4qc1tv + 0kHnMlrhrT65NT39NzIFwM5xN1rZOKLwHTPZGtfq8kzpk37jtNuLHq7tN/0xnWRD+y9Jv8ryQ/duT42t + PZnXs8Ubmmw1/v0hZGC29VZV/BziUYwtATAWRVHuf2nr8Dz66ehcm4fn/AJYqIptvUlPf/wl2jo8b/qj + Out///zbxtf/0ogBELe+14l+OjoXvwx2WJqd1qsHdyp9weSLzbcM/QqYa0/oX3/8jRUBULiff7TQHnu0 + 0NZF7yraPDzX97tnPAtuSBXbepOY+BdnywBQKqED6Oe024u2Ds/1Zq9DGNSkim29SQedy+iz735u+qM6 + b+3xfOm7MEdVSQDEHXQuozd7HW0dnvMUoQKTrXGtPZ6v/JnyabcXffbdz8x8SnD0zaeNnAHQT+UBELdz + 3I3+tt9heFiSKrf1xpk9/nRzxdm0/pdqDoA48ySB7aOjqXJbbxIT//IUPUK9bI0FgGGGhzxJyK6OYZ/B + xL9cdcxq8mg8AOIuelfR+l6H4eEAVW7rTVrf67DNt2T//tMnjZ0B0I9VARB32u1Fb96HAcPD6rf1JjHx + L9/NqZb+/adPrCl+yeIAiAv9SUIVb+sd5KBzyYs9KlDn0i0rJwIgLrRtyHVs641j4l8d29b/koMBEOf7 + NuQ6tvUmffHDfrRz3G36o3vJtvW/5HgAGOZJwt/2O/Lll7eJx0UrG0e81aciNq7/JQvOBizDZGt87NFC + W48W2l5sQ26iVVzf61D8FZqz4AWg/XjRAaQ57fai73fPnBke1rWtN2nnuBt98cN+0x/fa6vLM3q2eMO6 + DsDrAIg76FxG3++eWTs8nGtP6NXnd2p/TzwT/3r864+/afwMgH6CCYA4254k1LmtN46Jfz0mW+M6+uZT + 64pfCjQADBu2ITf5bJiJfz2WZqe19njeygDwYgg4qvjwsIltyE2uC1c2jij+mtjy/r9+gu4A0pgnCd/v + nlUyPKx7W28Se/zrZev6XyIAhip7G3Ld23qTmPjXz5YXgPZDAORQ9IUmTQ37DCb+9bt3e0p//8NdAsA3 + W4fn0Zv9TubhYRPbeuOY+Dejzq9vj4IAKCjLkwQb3gLz2Xc/RxR//f7+h7tWnAGYhgAoUb9tyDZ8A4w9 + /s2xef0vEQCVOe32IkmNf/vr9e5Z9GLzbdN/HUGyff0vBb4PoEpNF770bk5B8TfH5uf/RqbDQeGeg85l + 9DXP+hs117bzG4BxBICHOLnXDjYdAZaGAPDQw7V9J77+7LO59oQ1p/8MQgB4ZmXjiMd9FnDh7i8RAF55 + vXvG4z5LuDAAlAgA77gweAqBKx0A+wA8ZMN7DkJm2wGggxAAAbDtDUi+s/EAkDQEQGBCP2WpDjZs/86K + AAiYD69Qt5GNB4CkIQAg6frcYOe4y1JhRLYeAJKGAEBfZm6wc9xlqZCDS+t/iS8DIcXS7PTY0uy0pP/M + DXaOuywVhvjEkef/Bh0AcjFzA9Md4DqX1v8SAYACmBtcZ/MBIGkIAJQm9LmBzQeApGEGgNKEPjdwZf9/ + HB0AKhfK3MDmA0DSEACola9zAxfX/xIBgIb5Mjdwcf0vMQNAw3yZG7j6NWw6AFjJtbmB7QeApCEAYD0X + 5ga2HwCShgCAc2ybG7hwAEgaZgCeOehcRjenWk68kXZUts0NXHz+b9ABeMacA7g0O60v77a1NDvtbRAk + mbnB7km31lehubr+lwgA73zxw34UH5pNtsb1aKGtLxfazm1SKcLMDf7npFv5q9BcXf9LBIB3kgEQd3Oq + pa8Wb2hpdtqpb6yVYevwPDKdQZlzA5fX/xIB4J1BARC3NDut392ZdubddWU66FxGW4fv3phcdG7wbPGG + VpdnnP07JAA8kzUAjMnWuJZnp/XV4o2glghG0bnB2uN5p+csBIBn8gZAXMhLBGm0ucHRN586/cSFAPBM + kQCIu3d7Sr+/29by7LTTv+BFDJsbuHQASBoCwDNlBYBhlgi/v9t29lFXGfrNDVxf/0sEgHfKDoC4m1Mt + ffn+kWKISwTDzA3u3Z5yfm5CAHimygCIm2tP6KvFG0EvEXzA6cAYyUHnUisbR3q4tt/0paAAAgCFvPr8 + TtOXgAIIAIzs+f1bzq+BQ0cAYCRz7Qk9v3+L4nccAYCR0Pr7gQBAbrT+/iAAkAutv18IAORC6+8XAgCZ + 0fr7hwBAJrT+fiIAkAmtv58IAAxF6+8vAgAD0fr7jQDAQLT+fiMAkIrW338EAPqi9Q8DAeCZ0/8r5533 + tP5hIAA8U8ahF7T+4SAAcA2tf1gIAFxD6x8WAgAf0PqHhwCAJFr/UBEAkETrHyoCALT+ASMAAkfrHzYC + IHC0/mEjAAJG6w8CIFC0/pAIgGDR+kMiAIJE6w+DAAgMrT/iCIDA0PojjgAICK0/kgiAQND6ox8CIBC0 + /uiHAAgArT/SEACeo/XHIASA52j9MQgB4DFafwxDAHiK1h9ZEACeovVHFgSAh2j9kRUB4JHTbi+i9Uce + Y1EUNX0NKMlptxdd9K64+yMzAgAIGEsAIGAEABAwAgAIGAEABIwAAAJGAAABIwCAgBEAQMAIACBgBAAQ + sP8HnX+c8U4DeOMAAAAASUVORK5CYII= + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 45afa2e7..8bf700ea 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -65,6 +65,7 @@ +