Preliminary QR code generation support, via QRCoder.

This commit is contained in:
Michael Scire 2016-11-16 00:11:41 -08:00
parent 8310edc544
commit 6c5f58d73d
9 changed files with 1687 additions and 4 deletions

View file

@ -10,6 +10,7 @@ using System.Media;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using PKHeX.Saves.Substructures;
namespace PKHeX
{
@ -1570,7 +1571,18 @@ namespace PKHeX
PKM pkx = preparePKM();
byte[] ekx = pkx.EncryptedBoxData;
const string server = "http://loadcode.projectpokemon.org/b1s1.html#"; // Rehosted with permission from LC/MS -- massive thanks!
Image qr = QR.getQRImage(ekx, pkx.Format == 6 ? server : "null/#"); // pls no use QR on non gen6 -- bad user!
Image qr;
switch (pkx.Format)
{
case 6:
qr = QR.getQRImage(ekx, server);
break;
case 7:
qr = QR7.GenerateQRCode7((PK7) pkx);
break;
default:
return;
}
if (qr == null) return;

View file

@ -42,7 +42,7 @@
this.PB_QR.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.PB_QR.Location = new System.Drawing.Point(2, 1);
this.PB_QR.Name = "PB_QR";
this.PB_QR.Size = new System.Drawing.Size(365, 365);
this.PB_QR.Size = new System.Drawing.Size(405, 405);
this.PB_QR.TabIndex = 0;
this.PB_QR.TabStop = false;
this.PB_QR.Click += new System.EventHandler(this.PB_QR_Click);
@ -50,7 +50,7 @@
// FontLabel
//
this.FontLabel.AutoSize = true;
this.FontLabel.Location = new System.Drawing.Point(348, 353);
this.FontLabel.Location = new System.Drawing.Point(388, 393);
this.FontLabel.Name = "FontLabel";
this.FontLabel.Size = new System.Drawing.Size(19, 13);
this.FontLabel.TabIndex = 1;
@ -61,7 +61,7 @@
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(369, 367);
this.ClientSize = new System.Drawing.Size(409, 407);
this.Controls.Add(this.FontLabel);
this.Controls.Add(this.PB_QR);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;

View file

@ -213,6 +213,7 @@
<Compile Include="Saves\SaveFile.cs" />
<Compile Include="MysteryGifts\WC6.cs" />
<Compile Include="Saves\Substructures\MemeCrypto.cs" />
<Compile Include="Saves\Substructures\QR7.cs" />
<Compile Include="Saves\Substructures\ShadowInfo.cs" />
<Compile Include="Saves\Substructures\StrategyMemo.cs" />
<Compile Include="Settings.cs" />
@ -441,6 +442,10 @@
<Compile Include="Util\ImageUtil.cs" />
<Compile Include="Util\NetUtil.cs" />
<Compile Include="Util\PathUtil.cs" />
<Compile Include="Util\QRCoder\AbstractQRCode.cs" />
<Compile Include="Util\QRCoder\QRCode.cs" />
<Compile Include="Util\QRCoder\QRCodeData.cs" />
<Compile Include="Util\QRCoder\QRCodeGenerator.cs" />
<Compile Include="Util\RandUtil.cs" />
<Compile Include="Util\ReflectUtil.cs" />
<Compile Include="Util\StringUtil.cs" />

View file

@ -0,0 +1,92 @@
using System;
using System.Drawing;
using System.Linq;
using QRCoder;
namespace PKHeX.Saves.Substructures
{
public class QR7
{
private static bool hasGenderDifferences(int species)
{
var gendered = new[]
{
3, 12, 19, 20, 25, 26, 41, 42, 44, 45, 64, 65, 84, 85, 97, 111, 112, 118, 119, 123, 129, 130, 154, 165, 166,
178, 185, 186, 190, 194, 195, 198, 202, 203, 207, 208, 212, 214, 215, 217, 221, 224, 229, 232, 255, 256,
257, 267, 269, 272, 274, 275, 307, 308, 315, 316, 317, 322, 323, 332, 350, 369, 396, 397, 398, 399, 400,
401, 402, 403, 404, 405, 407, 415, 417, 418, 419, 424, 443, 444, 445, 449, 450, 453, 454, 456, 457, 459,
460, 461, 464, 465, 473, 521, 592, 593, 668, 678
};
return gendered.Contains(species);
}
private static byte[] GetRawQR(int species, int formnum, bool shiny, int gender)
{
var basedata = "FFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000000000D20200000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000".ToByteArray();
BitConverter.GetBytes((ushort)species).CopyTo(basedata, 0x28);
basedata[0x2A] = (byte)formnum;
basedata[0x2C] = (byte)(shiny ? 1 : 0);
var forme_index = PersonalTable.SM[species].FormeIndex(species, formnum);
var raw_gender = PersonalTable.SM[forme_index].Gender;
switch (raw_gender)
{
case 0:
basedata[0x2D] = 0;
basedata[0x2B] = 0;
break;
case 0xFE:
basedata[0x2D] = 0;
basedata[0x2B] = 1;
break;
case 0xFF:
basedata[0x2D] = 0;
basedata[0x2B] = 2;
break;
default:
basedata[0x2D] = (byte)(hasGenderDifferences(species) ? 0 : 1);
basedata[0x2B] = (byte)gender;
break;
}
return basedata;
}
private static byte[] GenerateQRData(PK7 pk7, int box = 0, int slot = 0)
{
if (box > 31)
box = 31;
if (slot > 29)
slot = 29;
if (box < 0)
box = 0;
if (slot < 0)
slot = 0;
byte[] data = new byte[0x1A2];
BitConverter.GetBytes(0x454B4F50).CopyTo(data, 0); // POKE magic
data[0x5] = 0xFF; // QR Type
BitConverter.GetBytes(box).CopyTo(data, 0x8);
BitConverter.GetBytes(slot).CopyTo(data, 0xC);
pk7.EncryptedPartyData.CopyTo(data, 0x30); // Copy in pokemon data
GetRawQR(pk7.Species, pk7.AltForm, pk7.IsShiny, pk7.Gender).CopyTo(data, 0x140);
BitConverter.GetBytes((ushort) SaveUtil.check16(data.Take(0x1A0).ToArray(), 0)).CopyTo(data, 0x1A0);
return data;
}
public static Bitmap GenerateQRCode7(PK7 pk7, int box = 0, int slot = 0)
{
byte[] data = GenerateQRData(pk7, box, slot);
using (var generator = new QRCodeGenerator())
using (var qr_data = generator.CreateQRCode(data))
using (var qr_code = new QRCode(qr_data))
return qr_code.GetGraphic(4);
}
public static Bitmap GenerateQRCode(byte[] data, int ppm = 4)
{
using (var generator = new QRCodeGenerator())
using (var qr_data = generator.CreateQRCode(data))
using (var qr_code = new QRCode(qr_data))
return qr_code.GetGraphic(ppm);
}
}
}

View file

@ -0,0 +1,14 @@
// From: https://github.com/codebude/QRCoder
namespace QRCoder
{
public abstract class AbstractQRCode<T>
{
protected QRCodeData QrCodeData { get; set; }
protected AbstractQRCode(QRCodeData data) {
this.QrCodeData = data;
}
public abstract T GetGraphic(int pixelsPerModule);
}
}

View file

@ -0,0 +1,138 @@
using System.Drawing;
using System.Drawing.Drawing2D;
// From: https://github.com/codebude/QRCoder
namespace QRCoder
{
using System;
public class QRCode : AbstractQRCode<Bitmap>, IDisposable
{
public QRCode(QRCodeData data) : base(data) {}
public override Bitmap GetGraphic(int pixelsPerModule)
{
return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, true);
}
public Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true)
{
return this.GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones);
}
public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true)
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
var offset = drawQuietZones ? 0 : 4 * pixelsPerModule;
var bmp = new Bitmap(size, size);
var gfx = Graphics.FromImage(bmp);
for (var x = 0; x < size + offset; x = x + pixelsPerModule)
{
for (var y = 0; y < size + offset; y = y + pixelsPerModule)
{
var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule)/pixelsPerModule - 1][(x + pixelsPerModule)/pixelsPerModule - 1];
if (module)
{
gfx.FillRectangle(new SolidBrush(darkColor), new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule));
}
else
{
gfx.FillRectangle(new SolidBrush(lightColor), new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule));
}
}
}
gfx.Save();
return bmp;
}
public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon=null, int iconSizePercent=15, int iconBorderWidth = 6, bool drawQuietZones = true)
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
var offset = drawQuietZones ? 0 : 4 * pixelsPerModule;
var bmp = new Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var gfx = Graphics.FromImage(bmp);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.CompositingQuality = CompositingQuality.HighQuality;
gfx.Clear(lightColor);
var drawIconFlag = icon != null && iconSizePercent>0 && iconSizePercent<=100;
GraphicsPath iconPath = null;
float iconDestWidth=0, iconDestHeight=0, iconX=0, iconY=0;
if (drawIconFlag)
{
iconDestWidth = iconSizePercent * bmp.Width / 100f;
iconDestHeight = drawIconFlag ? iconDestWidth * icon.Height / icon.Width : 0;
iconX = (bmp.Width - iconDestWidth) / 2;
iconY = (bmp.Height - iconDestHeight) / 2;
var centerDest = new RectangleF(iconX - iconBorderWidth, iconY - iconBorderWidth, iconDestWidth + iconBorderWidth * 2, iconDestHeight + iconBorderWidth * 2);
iconPath = this.CreateRoundedRectanglePath(centerDest, iconBorderWidth * 2);
}
var lightBrush = new SolidBrush(lightColor);
var darkBrush = new SolidBrush(darkColor);
for (var x = 0; x < size+offset; x = x + pixelsPerModule)
{
for (var y = 0; y < size + offset; y = y + pixelsPerModule)
{
var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule)/pixelsPerModule - 1][(x + pixelsPerModule)/pixelsPerModule - 1];
if (module)
{
var r = new Rectangle(x-offset, y-offset, pixelsPerModule, pixelsPerModule);
if (drawIconFlag)
{
var region = new Region(r);
region.Exclude(iconPath);
gfx.FillRegion(darkBrush, region);
}
else
{
gfx.FillRectangle(darkBrush, r);
}
}
else
gfx.FillRectangle(lightBrush, new Rectangle(x-offset, y-offset, pixelsPerModule, pixelsPerModule));
}
}
if (drawIconFlag)
{
var iconDestRect = new RectangleF(iconX, iconY, iconDestWidth, iconDestHeight);
gfx.DrawImage(icon, iconDestRect, new RectangleF(0, 0, icon.Width, icon.Height), GraphicsUnit.Pixel);
}
gfx.Save();
return bmp;
}
internal GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius)
{
var roundedRect = new GraphicsPath();
roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90);
roundedRect.AddLine(rect.X + cornerRadius, rect.Y, rect.Right - cornerRadius * 2, rect.Y);
roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y, cornerRadius * 2, cornerRadius * 2, 270, 90);
roundedRect.AddLine(rect.Right, rect.Y + cornerRadius * 2, rect.Right, rect.Y + rect.Height - cornerRadius * 2);
roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y + rect.Height - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90);
roundedRect.AddLine(rect.Right - cornerRadius * 2, rect.Bottom, rect.X + cornerRadius * 2, rect.Bottom);
roundedRect.AddArc(rect.X, rect.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90);
roundedRect.AddLine(rect.X, rect.Bottom - cornerRadius * 2, rect.X, rect.Y + cornerRadius * 2);
roundedRect.CloseFigure();
return roundedRect;
}
public void Dispose()
{
this.QrCodeData = null;
}
}
}

View file

@ -0,0 +1,36 @@
using System.Collections;
using System.Collections.Generic;
// From: https://github.com/codebude/QRCoder
namespace QRCoder
{
using System;
public class QRCodeData : IDisposable
{
public List<BitArray> ModuleMatrix { get; set; }
public QRCodeData(int version)
{
this.Version = version;
var size = ModulesPerSideFromVersion(version);
this.ModuleMatrix = new List<BitArray>();
for (var i = 0; i < size; i++)
this.ModuleMatrix.Add(new BitArray(size));
}
public int Version { get; private set; }
private static int ModulesPerSideFromVersion(int version)
{
return 21 + (version - 1) * 4;
}
public void Dispose()
{
this.ModuleMatrix = null;
this.Version = 0;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,10 @@ PKHeX is a Windows Forms application which requires .NET Framework v4.0.
The executable can be built with any compiler that supports C# 6.0.
## Dependencies
PKHeX's QR code generation code is taken from [QRCoder](https://github.com/codebude/QRCoder), which is licensed under [the MIT license](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt).
### IDE
PKHeX can be opened with IDEs such as [Visual Studio](https://www.visualstudio.com/) or [MonoDevelop](http://www.monodevelop.com/) by opening the .sln or .csproj file.