KAZIK/build/PokerGameSetupGui.cs

581 lines
24 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.IO.Compression;
using System.Linq;
using Microsoft.Win32;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PokerGameSetup
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new InstallerForm());
}
}
sealed class InstallerForm : Form
{
const string Marker = "<<POKER_GAME_GUI_PAYLOAD_ZIP>>";
const string ExpectedPayloadSha256 = "718663039D0CCB6EE83C62D23C8F8F7A6D4C596135E75F757C101C568E8473E9";
readonly Color bg = Color.FromArgb(15, 12, 10);
readonly Color panel = Color.FromArgb(28, 22, 18);
readonly Color gold = Color.FromArgb(236, 186, 75);
readonly Color red = Color.FromArgb(164, 26, 32);
readonly Color green = Color.FromArgb(29, 135, 64);
readonly Color text = Color.FromArgb(245, 236, 205);
CasinoArtPanel art;
Panel page;
Button backButton;
Button nextButton;
Button cancelButton;
int step;
TextBox folderBox;
CasinoProgress progress;
Label progressText;
CheckBox launchCheck;
string installedPath;
bool installing;
public InstallerForm()
{
Text = "Assembler Casino Setup";
try { Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath); } catch { }
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
ClientSize = new Size(820, 500);
BackColor = bg;
Font = new Font("Segoe UI", 10f);
art = new CasinoArtPanel { Dock = DockStyle.Left, Width = 260 };
Controls.Add(art);
page = new Panel { Left = 280, Top = 28, Width = 500, Height = 365, BackColor = panel };
page.Paint += (s, e) =>
{
using (var pen = new Pen(Color.FromArgb(88, gold), 2))
e.Graphics.DrawRectangle(pen, 0, 0, page.Width - 1, page.Height - 1);
};
Controls.Add(page);
backButton = MakeButton("Назад");
backButton.Left = 406; backButton.Top = 428; backButton.Click += (s, e) => ShowStep(step - 1);
Controls.Add(backButton);
nextButton = MakeButton("Далее");
nextButton.Left = 532; nextButton.Top = 428; nextButton.Click += NextClicked;
Controls.Add(nextButton);
cancelButton = MakeButton("Отмена");
cancelButton.Left = 658; cancelButton.Top = 428; cancelButton.Click += (s, e) => { if (!installing) Close(); };
Controls.Add(cancelButton);
ShowStep(0);
}
Button MakeButton(string caption)
{
var b = new Button
{
Text = caption,
Width = 108,
Height = 38,
FlatStyle = FlatStyle.Flat,
BackColor = Color.FromArgb(62, 43, 20),
ForeColor = text,
Font = new Font("Segoe UI", 10f, FontStyle.Bold),
Cursor = Cursors.Hand
};
b.FlatAppearance.BorderColor = gold;
b.FlatAppearance.MouseOverBackColor = Color.FromArgb(94, 61, 24);
b.FlatAppearance.MouseDownBackColor = Color.FromArgb(120, 71, 22);
return b;
}
Label MakeLabel(string caption, int x, int y, int w, int h, float size, FontStyle style)
{
var l = new Label
{
Text = caption,
Left = x,
Top = y,
Width = w,
Height = h,
ForeColor = text,
BackColor = Color.Transparent,
Font = new Font("Segoe UI", size, style)
};
page.Controls.Add(l);
return l;
}
void ShowStep(int value)
{
step = Math.Max(0, Math.Min(3, value));
page.Controls.Clear();
art.Stage = step;
art.Invalidate();
backButton.Enabled = step > 0 && !installing;
nextButton.Enabled = !installing;
cancelButton.Enabled = !installing;
nextButton.Text = step == 3 ? "Готово" : "Далее";
if (step == 0) WelcomePage();
if (step == 1) FolderPage();
if (step == 2) InstallPage();
if (step == 3) FinishPage();
}
void WelcomePage()
{
MakeLabel("Добро пожаловать", 32, 32, 430, 42, 22f, FontStyle.Bold);
MakeLabel("Установщик Assembler Casino", 34, 82, 420, 28, 13f, FontStyle.Regular);
MakeLabel("Внутри уже лежит вся игра: exe, арты, звуки, анимации, музыка башни и служебные файлы.", 34, 136, 420, 72, 10.5f, FontStyle.Regular);
MakeLabel("Сейчас выберем папку, красиво распакуем казино и положим рядом нормальный exe для запуска.", 34, 220, 420, 60, 10.5f, FontStyle.Regular);
MakeLabel("Нажми «Далее», если готов открыть двери.", 34, 315, 420, 26, 10.5f, FontStyle.Bold);
}
void FolderPage()
{
MakeLabel("Куда установить игру?", 32, 32, 430, 42, 22f, FontStyle.Bold);
MakeLabel("Можно выбрать любую папку. По умолчанию игра ставится на рабочий стол.", 34, 86, 420, 50, 10.5f, FontStyle.Regular);
folderBox = new TextBox
{
Left = 34,
Top = 160,
Width = 330,
Height = 28,
Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "покер"),
BackColor = Color.FromArgb(9, 9, 9),
ForeColor = text,
BorderStyle = BorderStyle.FixedSingle
};
page.Controls.Add(folderBox);
var browse = MakeButton("Обзор");
browse.Left = 374; browse.Top = 154; browse.Width = 86;
browse.Parent = page;
browse.Click += (s, e) =>
{
using (var dialog = new FolderBrowserDialog())
{
dialog.Description = "Выбери папку для Assembler Casino";
dialog.SelectedPath = Directory.Exists(folderBox.Text) ? folderBox.Text : Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
if (dialog.ShowDialog(this) == DialogResult.OK)
folderBox.Text = Path.Combine(dialog.SelectedPath, "покер");
}
};
MakeLabel("Сохранения игроков будут храниться отдельно в AppData, поэтому переустановка игру не обнулит.", 34, 230, 420, 70, 10.5f, FontStyle.Regular);
}
void InstallPage()
{
MakeLabel("Установка", 32, 32, 430, 42, 22f, FontStyle.Bold);
MakeLabel("Казино распаковывается. Фишки на стол, карты в колоду, барабаны на место.", 34, 86, 420, 52, 10.5f, FontStyle.Regular);
progressText = MakeLabel("Ожидание старта...", 34, 166, 420, 28, 10.5f, FontStyle.Bold);
progress = new CasinoProgress { Left = 34, Top = 208, Width = 420, Height = 34 };
page.Controls.Add(progress);
BeginInstall();
}
void FinishPage()
{
MakeLabel("Готово", 32, 32, 430, 42, 22f, FontStyle.Bold);
MakeLabel("Assembler Casino установлено. Можно запускать и проверять, как оно выглядит уже на другом компьютере.", 34, 86, 420, 70, 10.5f, FontStyle.Regular);
MakeLabel("Удачи за столами. И да, казино все еще подозрительно смотрит на чит-коды.", 34, 176, 420, 48, 10.5f, FontStyle.Regular);
launchCheck = new CheckBox
{
Text = "Запустить игру после закрытия установщика",
Left = 34,
Top = 275,
Width = 390,
Height = 28,
Checked = true,
BackColor = Color.Transparent,
ForeColor = text
};
page.Controls.Add(launchCheck);
cancelButton.Text = "Закрыть";
}
async void BeginInstall()
{
installing = true;
backButton.Enabled = nextButton.Enabled = cancelButton.Enabled = false;
try
{
var target = folderBox != null && !string.IsNullOrWhiteSpace(folderBox.Text)
? folderBox.Text.Trim()
: Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "покер");
await Task.Run(() => ExtractPayload(target));
installedPath = target;
installing = false;
ShowStep(3);
}
catch (Exception ex)
{
installing = false;
backButton.Enabled = nextButton.Enabled = cancelButton.Enabled = true;
MessageBox.Show(this, ex.Message, "Ошибка установки", MessageBoxButtons.OK, MessageBoxIcon.Error);
ShowStep(1);
}
}
void ExtractPayload(string target)
{
Directory.CreateDirectory(target);
Directory.CreateDirectory(Path.Combine(target, "runtime"));
var exe = Assembly.GetExecutingAssembly().Location;
var bytes = File.ReadAllBytes(exe);
var marker = System.Text.Encoding.UTF8.GetBytes(Marker);
var index = FindMarker(bytes, marker);
if (index < 0) throw new InvalidOperationException("В установщике не найден архив игры.");
var payloadBytes = bytes.Skip(index + marker.Length).ToArray();
VerifyPayloadIntegrity(payloadBytes);
var tempZip = Path.Combine(Path.GetTempPath(), "assembler_casino_payload_" + Guid.NewGuid().ToString("N") + ".zip");
File.WriteAllBytes(tempZip, payloadBytes);
try
{
using (var zip = ZipFile.OpenRead(tempZip))
{
var files = zip.Entries.Where(e => !string.IsNullOrEmpty(e.Name)).ToList();
for (int i = 0; i < files.Count; i++)
{
var entry = files[i];
var full = Path.GetFullPath(Path.Combine(target, entry.FullName));
if (!full.StartsWith(Path.GetFullPath(target), StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("Архив содержит некорректный путь.");
Directory.CreateDirectory(Path.GetDirectoryName(full));
entry.ExtractToFile(full, true);
int percent = (int)((i + 1) * 100.0 / Math.Max(1, files.Count));
SetProgress(percent, "Устанавливаем: " + entry.FullName.Replace('/', '\\'));
}
}
var oldBat = Path.Combine(target, "Assembler Casino.bat");
try { if (File.Exists(oldBat)) File.Delete(oldBat); } catch { }
CreateDesktopShortcut(target);
RegisterUninstaller(target);
SetProgress(100, "Казино установлено.");
}
finally
{
try { File.Delete(tempZip); } catch { }
}
}
void VerifyPayloadIntegrity(byte[] payloadBytes)
{
if (string.IsNullOrWhiteSpace(ExpectedPayloadSha256) || ExpectedPayloadSha256 == "TO_BE_FILLED_BY_RELEASE_BUILD")
return;
using (var sha = SHA256.Create())
{
var hash = sha.ComputeHash(payloadBytes);
var actual = BitConverter.ToString(hash).Replace("-", "");
if (!actual.Equals(ExpectedPayloadSha256, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("Архив игры внутри установщика был изменен. Установка остановлена.");
}
}
void CreateDesktopShortcut(string target)
{
try
{
var shellType = Type.GetTypeFromProgID("WScript.Shell");
if (shellType == null) return;
dynamic shell = Activator.CreateInstance(shellType);
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
var linkPath = Path.Combine(desktop, "Assembler Casino.lnk");
dynamic shortcut = shell.CreateShortcut(linkPath);
var launcher = Path.Combine(target, "Assembler Casino.exe");
var icon = Path.Combine(target, "assets", "icons", "casino.ico");
shortcut.TargetPath = launcher;
shortcut.WorkingDirectory = target;
shortcut.IconLocation = File.Exists(icon) ? icon : launcher + ",0";
shortcut.Description = "Assembler Casino";
shortcut.Save();
}
catch { }
}
void RegisterUninstaller(string target)
{
try
{
using (var key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\AssemblerCasino"))
{
if (key == null) return;
var launcher = Path.Combine(target, "Assembler Casino.exe");
var uninstaller = Path.Combine(target, "Uninstall Assembler Casino.exe");
var icon = Path.Combine(target, "assets", "icons", "casino.ico");
key.SetValue("DisplayName", "Assembler Casino");
key.SetValue("DisplayVersion", "1.0");
key.SetValue("Publisher", "Assembler Casino");
key.SetValue("InstallLocation", target);
key.SetValue("DisplayIcon", File.Exists(icon) ? icon : launcher);
key.SetValue("UninstallString", "\"" + uninstaller + "\"");
key.SetValue("NoModify", 1, RegistryValueKind.DWord);
key.SetValue("NoRepair", 1, RegistryValueKind.DWord);
}
}
catch { }
}
int FindMarker(byte[] bytes, byte[] marker)
{
for (int i = 0; i <= bytes.Length - marker.Length; i++)
{
bool ok = true;
for (int j = 0; j < marker.Length; j++)
{
if (bytes[i + j] != marker[j]) { ok = false; break; }
}
if (ok) return i;
}
return -1;
}
void SetProgress(int value, string caption)
{
if (InvokeRequired)
{
BeginInvoke(new Action<int, string>(SetProgress), value, caption);
return;
}
if (progress != null) progress.Value = value;
if (progressText != null) progressText.Text = caption;
}
void NextClicked(object sender, EventArgs e)
{
if (step == 1)
{
if (folderBox == null || string.IsNullOrWhiteSpace(folderBox.Text))
{
MessageBox.Show(this, "Выбери папку установки.", "Assembler Casino", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
}
if (step == 2) return;
if (step == 3)
{
if (launchCheck != null && launchCheck.Checked)
{
var root = !string.IsNullOrWhiteSpace(installedPath) ? installedPath : folderBox.Text.Trim();
var path = Path.Combine(root, "Assembler Casino.exe");
if (File.Exists(path)) Process.Start(path);
}
Close();
return;
}
ShowStep(step + 1);
}
}
sealed class CasinoProgress : Control
{
int value;
public int Value
{
get { return value; }
set { this.value = Math.Max(0, Math.Min(100, value)); Invalidate(); }
}
public CasinoProgress()
{
DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var r = ClientRectangle;
r.Width -= 1; r.Height -= 1;
using (var b = new SolidBrush(Color.FromArgb(8, 8, 8))) e.Graphics.FillRectangle(b, r);
using (var p = new Pen(Color.FromArgb(236, 186, 75), 2)) e.Graphics.DrawRectangle(p, r);
var fill = new Rectangle(4, 4, (int)((Width - 9) * value / 100.0), Height - 9);
if (fill.Width > 0 && fill.Height > 0)
{
using (var brush = new LinearGradientBrush(fill, Color.FromArgb(30, 130, 60), Color.FromArgb(236, 186, 75), 0f))
e.Graphics.FillRectangle(brush, fill);
}
var caption = value + "%";
using (var font = new Font("Consolas", 10f, FontStyle.Bold))
using (var brush = new SolidBrush(Color.White))
{
var sz = e.Graphics.MeasureString(caption, font);
e.Graphics.DrawString(caption, font, brush, (Width - sz.Width) / 2, (Height - sz.Height) / 2);
}
}
}
sealed class CasinoArtPanel : Control
{
public int Stage { get; set; }
readonly Color gold = Color.FromArgb(236, 186, 75);
readonly Color red = Color.FromArgb(166, 24, 31);
readonly Color green = Color.FromArgb(20, 112, 54);
public CasinoArtPanel()
{
DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
using (var br = new LinearGradientBrush(ClientRectangle, Color.FromArgb(34, 12, 14), Color.FromArgb(7, 55, 30), 90f))
g.FillRectangle(br, ClientRectangle);
DrawTitle(g);
DrawCards(g, 28, 96);
DrawRoulette(g, 132, 235);
DrawChips(g, 48, 345);
DrawStageDots(g);
using (var pen = new Pen(Color.FromArgb(120, gold), 2))
g.DrawLine(pen, Width - 1, 0, Width - 1, Height);
}
void DrawTitle(Graphics g)
{
using (var f1 = new Font("Georgia", 20f, FontStyle.Bold))
using (var f2 = new Font("Segoe UI", 9f, FontStyle.Bold))
using (var b1 = new SolidBrush(gold))
using (var b2 = new SolidBrush(Color.FromArgb(235, 235, 220)))
{
g.DrawString("ASSEMBLER", f1, b1, 24, 24);
g.DrawString("CASINO SETUP", f2, b2, 62, 58);
}
}
void DrawCards(Graphics g, int x, int y)
{
DrawCard(g, x + 28, y + 8, -10, red, "A", "♥");
DrawCard(g, x + 76, y, 8, Color.Black, "K", "♠");
DrawCard(g, x + 124, y + 14, 14, red, "7", "♦");
}
void DrawCard(Graphics g, int x, int y, float angle, Color suitColor, string rank, string suit)
{
var state = g.Save();
g.TranslateTransform(x + 30, y + 42);
g.RotateTransform(angle);
var r = new Rectangle(-30, -42, 60, 84);
using (var path = RoundRect(r, 6))
using (var br = new SolidBrush(Color.FromArgb(245, 241, 225)))
using (var pen = new Pen(gold, 2))
{
g.FillPath(br, path);
g.DrawPath(pen, path);
}
using (var f = new Font("Georgia", 17f, FontStyle.Bold))
using (var b = new SolidBrush(suitColor))
{
g.DrawString(rank, f, b, -20, -32);
g.DrawString(suit, f, b, -10, 4);
}
g.Restore(state);
}
void DrawRoulette(Graphics g, int x, int y)
{
var outer = new Rectangle(x - 68, y - 68, 136, 136);
using (var br = new SolidBrush(Color.FromArgb(96, 48, 22))) g.FillEllipse(br, outer);
using (var p = new Pen(gold, 5)) g.DrawEllipse(p, outer);
for (int i = 0; i < 12; i++)
{
var a = i * 30.0 * Math.PI / 180.0;
var c = (i % 3 == 0) ? green : (i % 2 == 0 ? red : Color.FromArgb(12, 12, 12));
using (var p = new Pen(c, 8))
g.DrawLine(p, x, y, x + (float)Math.Cos(a) * 58, y + (float)Math.Sin(a) * 58);
}
using (var br = new SolidBrush(Color.FromArgb(30, 120, 50))) g.FillEllipse(br, x - 36, y - 36, 72, 72);
using (var p = new Pen(Color.FromArgb(255, 230, 150), 3)) g.DrawEllipse(p, x - 36, y - 36, 72, 72);
using (var br = new SolidBrush(Color.White)) g.FillEllipse(br, x + 18, y - 55, 12, 12);
using (var p = new Pen(Color.FromArgb(255, 230, 150), 4))
{
g.DrawLine(p, x, y, x + 44, y - 18);
g.DrawLine(p, x, y, x - 36, y + 26);
g.DrawLine(p, x, y, x + 8, y + 48);
}
}
void DrawChips(Graphics g, int x, int y)
{
DrawChip(g, x, y, red, "$");
DrawChip(g, x + 48, y + 28, green, "21");
DrawChip(g, x + 106, y + 2, Color.FromArgb(25, 70, 160), "VIP");
}
void DrawChip(Graphics g, int x, int y, Color c, string label)
{
using (var br = new SolidBrush(c)) g.FillEllipse(br, x, y, 54, 54);
using (var p = new Pen(Color.White, 4)) g.DrawEllipse(p, x + 5, y + 5, 44, 44);
using (var p = new Pen(gold, 2)) g.DrawEllipse(p, x + 1, y + 1, 52, 52);
using (var f = new Font("Segoe UI", 9f, FontStyle.Bold))
using (var b = new SolidBrush(Color.White))
{
var sz = g.MeasureString(label, f);
g.DrawString(label, f, b, x + 27 - sz.Width / 2, y + 27 - sz.Height / 2);
}
}
void DrawStageDots(Graphics g)
{
int y = Height - 44;
for (int i = 0; i < 4; i++)
{
var c = i <= Stage ? gold : Color.FromArgb(90, 80, 62);
using (var br = new SolidBrush(c)) g.FillEllipse(br, 78 + i * 28, y, 12, 12);
}
}
GraphicsPath RoundRect(Rectangle r, int radius)
{
int d = radius * 2;
var path = new GraphicsPath();
path.AddArc(r.X, r.Y, d, d, 180, 90);
path.AddArc(r.Right - d, r.Y, d, d, 270, 90);
path.AddArc(r.Right - d, r.Bottom - d, d, d, 0, 90);
path.AddArc(r.X, r.Bottom - d, d, d, 90, 90);
path.CloseFigure();
return path;
}
}
}