diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3a88943
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.vs
+Epoc/bin
+Epoc/obj
+zPakr/bin
+zPakr/obj
diff --git a/Epoc.sln b/Epoc.sln
new file mode 100644
index 0000000..685188e
--- /dev/null
+++ b/Epoc.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35707.178 d17.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Epoc", "Epoc\Epoc.csproj", "{E1C880E8-A830-45B5-9466-99CBEDD05031}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zPakr", "zPakr\zPakr.csproj", "{5DA149CE-856E-4940-AFC2-D19F9C02F718}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E1C880E8-A830-45B5-9466-99CBEDD05031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1C880E8-A830-45B5-9466-99CBEDD05031}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1C880E8-A830-45B5-9466-99CBEDD05031}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1C880E8-A830-45B5-9466-99CBEDD05031}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5DA149CE-856E-4940-AFC2-D19F9C02F718}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5DA149CE-856E-4940-AFC2-D19F9C02F718}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5DA149CE-856E-4940-AFC2-D19F9C02F718}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5DA149CE-856E-4940-AFC2-D19F9C02F718}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Epoc/Epoc-icon.ico b/Epoc/Epoc-icon.ico
new file mode 100644
index 0000000..90b8d55
--- /dev/null
+++ b/Epoc/Epoc-icon.ico
Binary files differ
diff --git a/Epoc/Epoc.csproj b/Epoc/Epoc.csproj
new file mode 100644
index 0000000..6554baf
--- /dev/null
+++ b/Epoc/Epoc.csproj
@@ -0,0 +1,35 @@
+
+
+
+ WinExe
+ net9.0-windows
+ enable
+ true
+ enable
+ Epoc-icon.ico
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/Epoc/Epoc.csproj.user b/Epoc/Epoc.csproj.user
new file mode 100644
index 0000000..5bac621
--- /dev/null
+++ b/Epoc/Epoc.csproj.user
@@ -0,0 +1,11 @@
+
+
+
+ <_LastSelectedProfileId>C:\Users\IvanDominguez\source\repos\Epoc\Epoc\Properties\PublishProfiles\FolderProfile.pubxml
+
+
+
+ Form
+
+
+
\ No newline at end of file
diff --git a/Epoc/EpocRunner.cs b/Epoc/EpocRunner.cs
new file mode 100644
index 0000000..d4b1e4d
--- /dev/null
+++ b/Epoc/EpocRunner.cs
@@ -0,0 +1,267 @@
+using System.Diagnostics;
+
+namespace Epoc
+{
+ public class EpocRunner
+ {
+ private const string EPOC_BASE = @"C:\Epoc32";
+ private const string EPOC_EMUL = $"{EPOC_BASE}\\Rel\\Epoc.exe";
+ private const string EPOC_CONF = $"{EPOC_BASE}\\Data\\Epoc.ini";
+ private const string EPOC_HELP = $"{EPOC_BASE}\\Rel\\Help\\oepoc.chm";
+
+ private const int DEF_ScreenHeight = 240;
+ private const int DEF_ScreenWidth = 640;
+ private const int DEF_PhysicalScreenHeight = 2858;
+ private const int DEF_PhysicalScreenWidth = 7620;
+ private const int DEF_ScreenOffsetX = 90;
+ private const int DEF_ScreenOffsetY = 51;
+ private const int DEF_LedGap = 5;
+ private const int DEF_LedOffsetX = 36;
+ private const int DEF_LedOffsetY = 45;
+ private const int DEF_LedSize = 14;
+ private const EpocPointerType DEF_PointerType = EpocPointerType.Pen;
+ private const int DEF_MegabytesOfFreeMemory = 64;
+
+ ///
+ /// Check if Epoc emulator is correctly set.
+ ///
+ /// Ture or false
+ public static bool CheckLocation()
+ {
+ // This version of epoc emulator needs to be on C:\Epoc32 directory.
+ if (!File.Exists(EPOC_EMUL))
+ return false;
+ return true;
+ }
+
+ #region Load / Save
+ public void LoadConf()
+ {
+ if (!File.Exists(EPOC_CONF))
+ return;
+ foreach (var line in File.ReadLines(EPOC_CONF))
+ {
+ var eqp = line.IndexOf(' ');
+ string key, value;
+ if (eqp == -1)
+ {
+ key = line;
+ value = "";
+ }
+ else
+ {
+ key = line[0..eqp].Trim();
+ value = line[(eqp + 1)..].Trim();
+ }
+ switch (key)
+ {
+ case "ScreenHeight":
+ ScreenHeight = Parse(value, DEF_ScreenHeight);
+ break;
+ case "ScreenWidth":
+ ScreenWidth = Parse(value, DEF_ScreenWidth);
+ break;
+ case "PhysicalScreenHeight":
+ PhysicalScreenHeight = Parse(value, DEF_PhysicalScreenHeight);
+ break;
+ case "PhysicalScreenWidth":
+ PhysicalScreenWidth = Parse(value, DEF_PhysicalScreenWidth);
+ break;
+ case "ScreenOffsetX":
+ ScreenOffsetX = Parse(value, DEF_ScreenOffsetX);
+ break;
+ case "ScreenOffsetY":
+ ScreenOffsetY = Parse(value, DEF_ScreenOffsetY);
+ break;
+ case "LedArrangeVertically":
+ LedArrangeVertically = true;
+ break;
+ case "LedArrangeHorizontally":
+ LedArrangeVertically = false;
+ break;
+ case "LedGap":
+ LedGap = Parse(value, DEF_LedGap);
+ break;
+ case "LedOffsetX":
+ LedOffsetX = Parse(value, DEF_LedOffsetX);
+ break;
+ case "LedOffsetY":
+ LedOffsetY = Parse(value, DEF_LedOffsetY);
+ break;
+ case "LedSize":
+ LedSize = Parse(value, DEF_LedSize);
+ break;
+ case "MegabytesOfFreeMemory":
+ MegabytesOfFreeMemory = Parse(value, DEF_MegabytesOfFreeMemory);
+ break;
+ case "PointerType":
+ PointerType = Parse(value, DEF_PointerType);
+ break;
+ case "_EPOC_DRIVE_C":
+ DriveC = value;
+ break;
+ case "_EPOC_DRIVE_D":
+ DriveD = value;
+ break;
+ case "_EPOC_DRIVE_E":
+ DriveE = value;
+ break;
+ case "_EPOC_DRIVE_F":
+ DriveF = value;
+ break;
+ case "_EPOC_DRIVE_G":
+ DriveG = value;
+ break;
+ case "_EPOC_DRIVE_Z":
+ DriveZ = value;
+ break;
+ }
+ }
+ }
+
+ public void SaveConf()
+ {
+ List lines = [];
+ if (File.Exists(EPOC_CONF))
+ lines.AddRange(File.ReadLines(EPOC_CONF));
+ for (int i = 0; i < lines.Count; i++)
+ lines[i] = lines[i].Trim();
+ SetINI(lines, "ScreenHeight", ScreenHeight, DEF_ScreenHeight);
+ SetINI(lines, "ScreenWidth", ScreenWidth, DEF_ScreenWidth);
+ SetINI(lines, "PhysicalScreenHeight", PhysicalScreenHeight, DEF_PhysicalScreenHeight);
+ SetINI(lines, "PhysicalScreenWidth", PhysicalScreenWidth, DEF_PhysicalScreenWidth);
+ SetINI(lines, "ScreenOffsetX", ScreenOffsetX, DEF_ScreenOffsetX);
+ SetINI(lines, "ScreenOffsetY", ScreenOffsetY, DEF_ScreenOffsetY);
+ SetINIFlag(lines, "LedArrangeVertically", "LedArrangeHorizontally", LedArrangeVertically, true);
+ SetINI(lines, "LedGap", LedGap, DEF_LedGap);
+ SetINI(lines, "LedOffsetX", LedOffsetX, DEF_LedOffsetX);
+ SetINI(lines, "LedOffsetY", LedOffsetY, DEF_LedOffsetY);
+ SetINI(lines, "LedSize", LedSize, DEF_LedSize);
+ SetINI(lines, "MegabytesOfFreeMemory", MegabytesOfFreeMemory, DEF_MegabytesOfFreeMemory);
+ SetINI(lines, "PointerType", PointerType, DEF_PointerType);
+ SetINI(lines, "_EPOC_DRIVE_C", DriveC, string.Empty);
+ SetINI(lines, "_EPOC_DRIVE_D", DriveD, string.Empty);
+ SetINI(lines, "_EPOC_DRIVE_E", DriveE, string.Empty);
+ SetINI(lines, "_EPOC_DRIVE_F", DriveF, string.Empty);
+ SetINI(lines, "_EPOC_DRIVE_G", DriveG, string.Empty);
+ SetINI(lines, "_EPOC_DRIVE_Z", DriveZ, string.Empty);
+ File.WriteAllLines(EPOC_CONF, lines);
+ }
+
+ private static int Parse(string value, int def)
+ {
+ if (int.TryParse(value, out var ival))
+ return ival;
+ return def;
+ }
+
+ private static EpocPointerType Parse(string value, EpocPointerType def)
+ {
+ return value switch
+ {
+ "PEN" => EpocPointerType.Pen,
+ "MOUSE" => EpocPointerType.Mouse,
+ _ => EpocPointerType.None,
+ };
+ }
+
+ private static void SetINI(List lines, string key, string value, string def)
+ {
+ value = value.Trim();
+ if (value == def)
+ return;
+ var newLine = $"{key} {value.Trim()}";
+ var lineNum = ConfLineOf(lines, key);
+ if (lineNum >= 0)
+ lines[lineNum] = newLine;
+ else
+ lines.Add(newLine);
+
+ }
+
+ private static void SetINI(List lines, string key, int value, int def)
+ {
+ SetINI(lines, key, value.ToString(), def.ToString());
+ }
+
+ private static void SetINI(List lines, string key, EpocPointerType pointer, EpocPointerType def)
+ {
+ SetINI(lines, key, PointerKey(pointer), PointerKey(def));
+ }
+
+ private static void SetINIFlag(List lines, string keyOn, string keyOff, bool value, bool def)
+ {
+ int toRemove = ConfLineOf(lines, keyOn);
+ if (toRemove >= 0)
+ lines.RemoveAt(toRemove);
+ toRemove = ConfLineOf(lines, keyOff);
+ if (toRemove >= 0)
+ lines.RemoveAt(toRemove);
+ if (value != def)
+ lines.Add(value ? keyOn : keyOff);
+ }
+
+ private static int ConfLineOf(List lines, string key)
+ {
+ key = key.Trim() + ' ';
+ for (int i = 0; i < lines.Count; i++)
+ {
+ var line = lines[i];
+ if (line.StartsWith(key))
+ return i;
+ }
+ return -1;
+ }
+
+ private static string PointerKey(EpocPointerType pointer)
+ {
+ return pointer switch
+ {
+ EpocPointerType.Pen => "PEN",
+ EpocPointerType.Mouse => "MOUSE",
+ _ => "NONE",
+ };
+ }
+ #endregion
+
+ public void Run(bool save = true)
+ {
+ if (save)
+ SaveConf();
+ Process.Start(EPOC_EMUL);
+ }
+
+ public void Help()
+ {
+ Process.Start(new ProcessStartInfo(EPOC_HELP)
+ {
+ UseShellExecute = true,
+ });
+ }
+
+ public int ScreenHeight { get; set; } = DEF_ScreenHeight;
+ public int ScreenWidth { get; set; } = DEF_ScreenWidth;
+ public int PhysicalScreenHeight { get; set; } = DEF_PhysicalScreenHeight;
+ public int PhysicalScreenWidth { get; set; } = DEF_PhysicalScreenWidth;
+ public int ScreenOffsetX { get; set; } = DEF_ScreenOffsetX;
+ public int ScreenOffsetY { get; set; } = DEF_ScreenOffsetY;
+
+ public bool LedArrangeVertically { get; set; } = true;
+ public int LedGap { get; set; } = DEF_LedGap;
+ public int LedOffsetX { get; set; } = DEF_LedOffsetX;
+ public int LedOffsetY { get; set; } = DEF_LedOffsetY;
+ public int LedSize { get; set; } = DEF_LedSize;
+ public int MegabytesOfFreeMemory { get; set; } = DEF_MegabytesOfFreeMemory;
+ public EpocPointerType PointerType { get; set; } = DEF_PointerType;
+
+ public string DriveC { get; set; } = string.Empty;
+ public string DriveD { get; set; } = string.Empty;
+ public string DriveE { get; set; } = string.Empty;
+ public string DriveF { get; set; } = string.Empty;
+ public string DriveG { get; set; } = string.Empty;
+ public string DriveZ { get; set; } = string.Empty;
+
+ }
+
+ public enum EpocPointerType { None, Pen, Mouse };
+}
diff --git a/Epoc/Form1.Designer.cs b/Epoc/Form1.Designer.cs
new file mode 100644
index 0000000..658b071
--- /dev/null
+++ b/Epoc/Form1.Designer.cs
@@ -0,0 +1,380 @@
+namespace Epoc
+{
+ partial class Form1
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
+ pbLogo = new PictureBox();
+ lbMemory = new Label();
+ lbDC = new Label();
+ tbDC = new TextBox();
+ btDC = new Button();
+ cbRAM = new ComboBox();
+ btDD = new Button();
+ tbDD = new TextBox();
+ lbDD = new Label();
+ btDE = new Button();
+ tbDE = new TextBox();
+ lbDE = new Label();
+ btDF = new Button();
+ tbDF = new TextBox();
+ lbDF = new Label();
+ btDG = new Button();
+ tbDG = new TextBox();
+ lbDG = new Label();
+ btDZ = new Button();
+ tbDZ = new TextBox();
+ lbDZ = new Label();
+ btGo = new Button();
+ btHelp = new Button();
+ ((System.ComponentModel.ISupportInitialize)pbLogo).BeginInit();
+ SuspendLayout();
+ //
+ // pbLogo
+ //
+ pbLogo.Image = (Image)resources.GetObject("pbLogo.Image");
+ pbLogo.Location = new Point(23, 29);
+ pbLogo.Margin = new Padding(4, 3, 4, 3);
+ pbLogo.Name = "pbLogo";
+ pbLogo.Size = new Size(134, 159);
+ pbLogo.SizeMode = PictureBoxSizeMode.AutoSize;
+ pbLogo.TabIndex = 0;
+ pbLogo.TabStop = false;
+ //
+ // lbMemory
+ //
+ lbMemory.AutoSize = true;
+ lbMemory.Location = new Point(174, 29);
+ lbMemory.Margin = new Padding(4, 0, 4, 0);
+ lbMemory.Name = "lbMemory";
+ lbMemory.Size = new Size(54, 23);
+ lbMemory.TabIndex = 1;
+ lbMemory.Text = "RAM:";
+ //
+ // lbDC
+ //
+ lbDC.AutoSize = true;
+ lbDC.Location = new Point(174, 72);
+ lbDC.Name = "lbDC";
+ lbDC.Size = new Size(26, 23);
+ lbDC.TabIndex = 3;
+ lbDC.Text = "C:";
+ //
+ // tbDC
+ //
+ tbDC.BackColor = Color.FromArgb(64, 64, 64);
+ tbDC.BorderStyle = BorderStyle.None;
+ tbDC.Font = new Font("Segoe UI", 10.2F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ tbDC.ForeColor = Color.White;
+ tbDC.Location = new Point(206, 72);
+ tbDC.Name = "tbDC";
+ tbDC.PlaceholderText = "(Default)";
+ tbDC.Size = new Size(201, 23);
+ tbDC.TabIndex = 4;
+ //
+ // btDC
+ //
+ btDC.FlatStyle = FlatStyle.Flat;
+ btDC.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btDC.Location = new Point(413, 70);
+ btDC.Name = "btDC";
+ btDC.Size = new Size(28, 27);
+ btDC.TabIndex = 5;
+ btDC.Text = "🔍";
+ btDC.UseVisualStyleBackColor = true;
+ btDC.Click += DriveButton_Click;
+ //
+ // cbRAM
+ //
+ cbRAM.BackColor = Color.FromArgb(64, 64, 64);
+ cbRAM.DropDownStyle = ComboBoxStyle.DropDownList;
+ cbRAM.FlatStyle = FlatStyle.Flat;
+ cbRAM.ForeColor = Color.White;
+ cbRAM.FormattingEnabled = true;
+ cbRAM.Items.AddRange(new object[] { " 4mb", "8 mb", "16 mb", "32 mb", "64 mb*", "128 mb", "256 mb" });
+ cbRAM.Location = new Point(235, 26);
+ cbRAM.Name = "cbRAM";
+ cbRAM.Size = new Size(206, 31);
+ cbRAM.TabIndex = 2;
+ cbRAM.SelectedIndexChanged += cbRAM_SelectedIndexChanged;
+ //
+ // btDD
+ //
+ btDD.FlatStyle = FlatStyle.Flat;
+ btDD.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btDD.Location = new Point(413, 103);
+ btDD.Name = "btDD";
+ btDD.Size = new Size(28, 27);
+ btDD.TabIndex = 8;
+ btDD.Text = "🔍";
+ btDD.UseVisualStyleBackColor = true;
+ btDD.Click += DriveButton_Click;
+ //
+ // tbDD
+ //
+ tbDD.BackColor = Color.FromArgb(64, 64, 64);
+ tbDD.BorderStyle = BorderStyle.None;
+ tbDD.Font = new Font("Segoe UI", 10.2F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ tbDD.ForeColor = Color.White;
+ tbDD.Location = new Point(206, 105);
+ tbDD.Name = "tbDD";
+ tbDD.Size = new Size(201, 23);
+ tbDD.TabIndex = 7;
+ //
+ // lbDD
+ //
+ lbDD.AutoSize = true;
+ lbDD.Location = new Point(174, 105);
+ lbDD.Name = "lbDD";
+ lbDD.Size = new Size(28, 23);
+ lbDD.TabIndex = 6;
+ lbDD.Text = "D:";
+ //
+ // btDE
+ //
+ btDE.FlatStyle = FlatStyle.Flat;
+ btDE.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btDE.Location = new Point(413, 136);
+ btDE.Name = "btDE";
+ btDE.Size = new Size(28, 27);
+ btDE.TabIndex = 11;
+ btDE.Text = "🔍";
+ btDE.UseVisualStyleBackColor = true;
+ btDE.Click += DriveButton_Click;
+ //
+ // tbDE
+ //
+ tbDE.BackColor = Color.FromArgb(64, 64, 64);
+ tbDE.BorderStyle = BorderStyle.None;
+ tbDE.Font = new Font("Segoe UI", 10.2F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ tbDE.ForeColor = Color.White;
+ tbDE.Location = new Point(206, 138);
+ tbDE.Name = "tbDE";
+ tbDE.Size = new Size(201, 23);
+ tbDE.TabIndex = 10;
+ //
+ // lbDE
+ //
+ lbDE.AutoSize = true;
+ lbDE.Location = new Point(174, 138);
+ lbDE.Name = "lbDE";
+ lbDE.Size = new Size(24, 23);
+ lbDE.TabIndex = 9;
+ lbDE.Text = "E:";
+ //
+ // btDF
+ //
+ btDF.FlatStyle = FlatStyle.Flat;
+ btDF.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btDF.Location = new Point(413, 169);
+ btDF.Name = "btDF";
+ btDF.Size = new Size(28, 27);
+ btDF.TabIndex = 14;
+ btDF.Text = "🔍";
+ btDF.UseVisualStyleBackColor = true;
+ btDF.Click += DriveButton_Click;
+ //
+ // tbDF
+ //
+ tbDF.BackColor = Color.FromArgb(64, 64, 64);
+ tbDF.BorderStyle = BorderStyle.None;
+ tbDF.Font = new Font("Segoe UI", 10.2F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ tbDF.ForeColor = Color.White;
+ tbDF.Location = new Point(206, 171);
+ tbDF.Name = "tbDF";
+ tbDF.Size = new Size(201, 23);
+ tbDF.TabIndex = 13;
+ //
+ // lbDF
+ //
+ lbDF.AutoSize = true;
+ lbDF.Location = new Point(174, 171);
+ lbDF.Name = "lbDF";
+ lbDF.Size = new Size(24, 23);
+ lbDF.TabIndex = 12;
+ lbDF.Text = "F:";
+ //
+ // btDG
+ //
+ btDG.FlatStyle = FlatStyle.Flat;
+ btDG.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btDG.Location = new Point(413, 202);
+ btDG.Name = "btDG";
+ btDG.Size = new Size(28, 27);
+ btDG.TabIndex = 17;
+ btDG.Text = "🔍";
+ btDG.UseVisualStyleBackColor = true;
+ btDG.Click += DriveButton_Click;
+ //
+ // tbDG
+ //
+ tbDG.BackColor = Color.FromArgb(64, 64, 64);
+ tbDG.BorderStyle = BorderStyle.None;
+ tbDG.Font = new Font("Segoe UI", 10.2F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ tbDG.ForeColor = Color.White;
+ tbDG.Location = new Point(206, 204);
+ tbDG.Name = "tbDG";
+ tbDG.Size = new Size(201, 23);
+ tbDG.TabIndex = 16;
+ //
+ // lbDG
+ //
+ lbDG.AutoSize = true;
+ lbDG.Location = new Point(174, 204);
+ lbDG.Name = "lbDG";
+ lbDG.Size = new Size(27, 23);
+ lbDG.TabIndex = 15;
+ lbDG.Text = "G:";
+ //
+ // btDZ
+ //
+ btDZ.FlatStyle = FlatStyle.Flat;
+ btDZ.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btDZ.Location = new Point(413, 235);
+ btDZ.Name = "btDZ";
+ btDZ.Size = new Size(28, 27);
+ btDZ.TabIndex = 20;
+ btDZ.Text = "🔍";
+ btDZ.UseVisualStyleBackColor = true;
+ btDZ.Click += DriveButton_Click;
+ //
+ // tbDZ
+ //
+ tbDZ.BackColor = Color.FromArgb(64, 64, 64);
+ tbDZ.BorderStyle = BorderStyle.None;
+ tbDZ.Font = new Font("Segoe UI", 10.2F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ tbDZ.ForeColor = Color.White;
+ tbDZ.Location = new Point(206, 237);
+ tbDZ.Name = "tbDZ";
+ tbDZ.PlaceholderText = "(Default)";
+ tbDZ.Size = new Size(201, 23);
+ tbDZ.TabIndex = 19;
+ //
+ // lbDZ
+ //
+ lbDZ.AutoSize = true;
+ lbDZ.Location = new Point(174, 237);
+ lbDZ.Name = "lbDZ";
+ lbDZ.Size = new Size(25, 23);
+ lbDZ.TabIndex = 18;
+ lbDZ.Text = "Z:";
+ //
+ // btGo
+ //
+ btGo.FlatStyle = FlatStyle.Flat;
+ btGo.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btGo.Location = new Point(329, 276);
+ btGo.Name = "btGo";
+ btGo.Size = new Size(112, 27);
+ btGo.TabIndex = 21;
+ btGo.Text = "▶️ Go!";
+ btGo.UseVisualStyleBackColor = true;
+ btGo.Click += btGo_Click;
+ //
+ // btHelp
+ //
+ btHelp.FlatStyle = FlatStyle.Flat;
+ btHelp.Font = new Font("Segoe UI", 7.8F, FontStyle.Regular, GraphicsUnit.Point, 0);
+ btHelp.Location = new Point(174, 276);
+ btHelp.Name = "btHelp";
+ btHelp.Size = new Size(70, 27);
+ btHelp.TabIndex = 22;
+ btHelp.Text = "❓ OPL";
+ btHelp.UseVisualStyleBackColor = true;
+ btHelp.Click += btHelp_Click;
+ //
+ // Form1
+ //
+ AutoScaleDimensions = new SizeF(10F, 23F);
+ AutoScaleMode = AutoScaleMode.Font;
+ BackColor = Color.FromArgb(24, 29, 32);
+ ClientSize = new Size(456, 317);
+ Controls.Add(btHelp);
+ Controls.Add(btGo);
+ Controls.Add(btDZ);
+ Controls.Add(tbDZ);
+ Controls.Add(lbDZ);
+ Controls.Add(btDG);
+ Controls.Add(tbDG);
+ Controls.Add(lbDG);
+ Controls.Add(btDF);
+ Controls.Add(tbDF);
+ Controls.Add(lbDF);
+ Controls.Add(btDE);
+ Controls.Add(tbDE);
+ Controls.Add(lbDE);
+ Controls.Add(btDD);
+ Controls.Add(tbDD);
+ Controls.Add(lbDD);
+ Controls.Add(cbRAM);
+ Controls.Add(btDC);
+ Controls.Add(tbDC);
+ Controls.Add(lbDC);
+ Controls.Add(lbMemory);
+ Controls.Add(pbLogo);
+ Font = new Font("Segoe UI", 10.2F, FontStyle.Bold, GraphicsUnit.Point, 0);
+ ForeColor = Color.FromArgb(255, 255, 192);
+ FormBorderStyle = FormBorderStyle.FixedSingle;
+ Icon = (Icon)resources.GetObject("$this.Icon");
+ Margin = new Padding(4, 3, 4, 3);
+ MaximizeBox = false;
+ Name = "Form1";
+ StartPosition = FormStartPosition.CenterScreen;
+ Text = "Epoc Emulator Launcher";
+ ((System.ComponentModel.ISupportInitialize)pbLogo).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private PictureBox pbLogo;
+ private Label lbMemory;
+ private Label lbDC;
+ private TextBox tbDC;
+ private Button btDC;
+ private ComboBox cbRAM;
+ private Button btDD;
+ private TextBox tbDD;
+ private Label lbDD;
+ private Button btDE;
+ private TextBox tbDE;
+ private Label lbDE;
+ private Button btDF;
+ private TextBox tbDF;
+ private Label lbDF;
+ private Button btDG;
+ private TextBox tbDG;
+ private Label lbDG;
+ private Button btDZ;
+ private TextBox tbDZ;
+ private Label lbDZ;
+ private Button btGo;
+ private Button btHelp;
+ }
+}
diff --git a/Epoc/Form1.cs b/Epoc/Form1.cs
new file mode 100644
index 0000000..7fadd68
--- /dev/null
+++ b/Epoc/Form1.cs
@@ -0,0 +1,127 @@
+using System.Reflection;
+using static Epoc.DriveRow;
+
+namespace Epoc
+{
+ public partial class Form1 : Form
+ {
+ private readonly EpocRunner epoc = new();
+ private readonly DriveRow driveC;
+ private readonly DriveRow driveD;
+ private readonly DriveRow driveE;
+ private readonly DriveRow driveF;
+ private readonly DriveRow driveG;
+ private readonly DriveRow driveZ;
+
+ public Form1()
+ {
+ InitializeComponent();
+ driveC = new DriveRow(lbDC, tbDC, btDC, () => epoc.DriveC, value => epoc.DriveC = value);
+ driveD = new DriveRow(lbDD, tbDD, btDD, () => epoc.DriveD, value => epoc.DriveD = value);
+ driveE = new DriveRow(lbDE, tbDE, btDE, () => epoc.DriveE, value => epoc.DriveE = value);
+ driveF = new DriveRow(lbDF, tbDF, btDF, () => epoc.DriveF, value => epoc.DriveF = value);
+ driveG = new DriveRow(lbDG, tbDG, btDG, () => epoc.DriveG, value => epoc.DriveG = value);
+ driveZ = new DriveRow(lbDZ, tbDZ, btDZ, () => epoc.DriveZ, value => epoc.DriveZ = value);
+
+ LoadConf();
+ if (!EpocRunner.CheckLocation())
+ MessageBox.Show("El emulador Epoc no Est� instalado en la carpeta correcta, Actualmente Epoc s�lo se puede instalar en c:\\Epoc32.\nLa aplicaci�n no funcionar�.", "Instalaci�n", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ private void LoadConf()
+ {
+ epoc.LoadConf();
+ int[] ctt = [4, 8, 16, 32, 64, 128, 256, int.MaxValue];
+ for (int i = 0; i < ctt.Length; i++)
+ {
+ if (ctt[i] > epoc.MegabytesOfFreeMemory)
+ {
+ cbRAM.SelectedIndex = Math.Max(i - 1, 0);
+ break;
+ }
+ }
+ foreach (var row in Rows)
+ row.Read();
+ }
+
+ private DriveRow RowOf(Control c)
+ {
+ foreach (var row in Rows)
+ {
+ if (c == row.Label || c == row.TextBox || c == row.Button)
+ return row;
+ }
+ throw new Exception("Not a row control");
+ }
+
+ private DriveRow[] Rows => [driveC, driveD, driveE, driveF, driveG, driveZ];
+
+ private void DriveButton_Click(object sender, EventArgs e)
+ {
+ var row = RowOf(sender as Control ?? throw new Exception("Not a control"));
+ var path = row.TextBox.Text;
+ if (string.IsNullOrWhiteSpace(path))
+ path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ FolderBrowserDialog fbd = new()
+ {
+ SelectedPath = path
+ };
+ if (fbd.ShowDialog() == DialogResult.OK)
+ row.TextBox.Text = fbd.SelectedPath;
+ }
+
+ private void btHelp_Click(object sender, EventArgs e)
+ {
+ epoc.Help();
+ }
+
+ private void btGo_Click(object sender, EventArgs e)
+ {
+ epoc.Run();
+ }
+
+ private void cbRAM_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ int[] ctt = [4, 8, 16, 32, 64, 128, 256, int.MaxValue];
+ epoc.MegabytesOfFreeMemory = ctt[cbRAM.SelectedIndex];
+ }
+ }
+
+ public class DriveRow
+ {
+ public delegate string PropertyGetter();
+ public delegate void PropertySetter(string path);
+
+ public DriveRow(Label label, TextBox textBox, Button button, PropertyGetter get, PropertySetter set)
+ {
+ Label = label;
+ TextBox = textBox;
+ Button = button;
+ Get = get;
+ Set = set;
+
+ TextBox.TextChanged += TextChanged;
+ }
+
+ private void TextChanged(object? sender, EventArgs e)
+ {
+ Write();
+ }
+
+ public void Read()
+ {
+ TextBox.Text = Get();
+ }
+
+ public void Write()
+ {
+ Set(TextBox.Text);
+ }
+
+ public Label Label { get; }
+ public TextBox TextBox { get; }
+ public Button Button { get; }
+ public PropertyGetter Get { get; }
+ public PropertySetter Set { get; }
+ }
+}
diff --git a/Epoc/Form1.resx b/Epoc/Form1.resx
new file mode 100644
index 0000000..29a1091
--- /dev/null
+++ b/Epoc/Form1.resx
@@ -0,0 +1,547 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ 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
+ 17MhkbaVI5W7mCWexqEWaoZ+J6TNqCCLr5EAAABtAAAAbR+Iq44npM6oHYOliyio0q0BFx9uAAAAbRRi
+ fXsbe5uGAAAAbQAAAG0AAABtAAAAXQAAAGwAAABtAAAAbQAAAG0moculIpS6mBhwjoEbepqGJ6TNqCah
+ yaQgjbGSKarVsQAAAG0AAABtJ6PMpx+KrZAmoMijIpG3lQAAAG0AAABtIY+0kyekzagdg6WLKavWsQIb
+ JW4AAABtF26MgCGQtZQAAABtAAAAbQAAAG0AAABsAAAAbQAAAG0AAABtAAAAbRJfeXokmL+cIpS6mCKU
+ upglncWhF26MgBVng30lncWgIY+zkySZwJwjlryZC0dbdBNifHsjmL6bI5W7mCKTuZckm8OeDU1idQMf
+ Km4hj7STI5W8mSKSuJYlnMSgGneWhAAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0NTGF1HYKkiiOXvZomoMmkJ6TNqCai
+ y6YmocqkJJnBnSGPtJMaepmFDk5kdgAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0HN0dxI5a8mSms17Iqrtq2K7LevCy1
+ 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
+ yaQqrtq2H4irjgAAAG0IOktyLLXjwiqu2rYgjLCRAAAAbQAAAG0YcY6BK7DduSmr1rIAAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0cgKKJK7Pgvh+K
+ rpAAAABtEl53eiy25MMprNezC0VZdAAAAG0fiq6QK7Heuyqt2bUZdZSDAAAAbQAAAG0AAABtAh8pbgAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAH
+ DG0steLBJ6TNqAACA20AAABtKKjSrSqu2rYgjLCRAAAAbQAAAG0pqdSvKq7atiim0KsLRlp0AAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABsAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbSmq1bEqrdm1AAYKbQAAAG0dgaKKLLXiwSip064GMkFwAAAAbQIdJ24rst+9Kq7atiCN
+ sZIAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbAAAAF0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtElx1eSagyKMAAQJtAAAAbQAEB20qr9u3Kq7athhxj4EAAABtAAAAbQxI
+ XXQstOLAKqzYtAAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABdAAAAMwAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbRZphX4ss+C+HoapjQAA
+ AG0AAABtAAAAbQMhLG8UZH58AAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAADMAAAADAAAAUAAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbRdt
+ ioAOUGZ2AAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABQAAAAAwAA
+ AAAAAAADAAAAMwAAAF0AAABsAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAGwAAABdAAAAMwAA
+ AAMAAAAAgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAIAAAAEoAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAADwAAADsAAABcAAAAawAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABrAAAAXAAAADsAAAAPAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAArAAAAaAAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABoAAAAKwAAAAAAAAAAAAAAAAAAACsAAABrAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtBzhIcQc4
+ SHEHN0dxAAAAbQAAAG0AAABtAAECbQc3R3EHOElxAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtBzdHcQY0RHEGM0NxAhghbgAAAG0AAABtAAAAbQAAAG0AAABtBjRDcQYzQ3EGM0NxBjRDcQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAawAAACsAAAAAAAAADwAAAGgAAABtAAAAbQAAAG0AAABtAAECbR6F
+ qIwnosumJqLLpiagyaQoqNOtJ6TOqR+KrY8AAABtGHORgiqu2rYop9KtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbR6FqIwmosumKKbQqyWex6IoptGrKKfSrSGPtJQBFh5uAAAAbQQqOG8lnMOfKq7atiWe
+ xqElnsahKq7atiWcw58EKjhvAAAAbQAAAG0AAABtAAAAbQAAAGgAAAAPAAAAOwAAAG0AAABtAAAAbQAA
+ AG0AAABtH4isjyqu2rYik7mXAAECbQAAAG0BDRRtII2xkiaiy6YYcI6BF2+MgCqu2rYpq9axH4msjx+J
+ rI8fiayPF22JfwAAAG0AAABtHoaojSqu2rYlncWgAAQGbQAAAG0BDRNtH4qukCqu2rYhj7SUBjREcSej
+ zKcqrtq2GXSTgwAAAG0AAABtHYOliyqu2rcmoMijAAAAbQAAAG0AAABtAAAAbQAAAG0AAAA7AAAAXAAA
+ AG0AAABtAAAAbQAAAG0AAABtJ6XPqSqu2rYnpM6oHoaojR2CpIodgqOKD1Nqdw9Ua3cAAABtF22KgCqu
+ 2rYpqdSvG3uchht7m4YfiayPKKnTrimp1K8MSl91JZ3GoSqv27cNTWJ1AAAAbQAAAG0AAABtC0VYcyqu
+ 2rYnpM6pDU1idSqv27clncWhAAAAbQAAAG0AAABtBCczbwY0RHEEKzhwAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABcAAAAawAAAG0AAABtAAAAbQAAAG0AAABtJ6XPqiqu2rYdg6WLHYKkih2CpIohkbaVJ6XOqSqu
+ 2rYjlr2aF22KgCqu2rYoqdOuAAAAbQAAAG0AAABtJZvDnyqu2rYVZ4J9JZvDnyqu2rcXb4yAAAAAbQAA
+ AG0AAABtC0RYcyqu2rYnpM6pDU1idSqv27cop9GrAQ0UbQAAAG0AAABtBCs4cBt7m4YbepqGAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABrAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtGneWhCqu2rUik7iWEVlxeAEU
+ HG4RWXF4JqDJpCqu2rYhj7OTF26LgCqu2rYoqNOuAAkObQEUHG4XbYqAKKbRqyqs2LMMSV50GniXhSqu
+ 2rYlncSgEFZueAEVHm4JQFNzJ6XPqSqt2bUXbYl/BjREcSejzKcqrtq2GXSTgwEWH24FLjxwIY6zkyqu
+ 2rYmn8ijAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAMEbRFZ
+ cXgkmcCcJqLLpSahyqUnosymJJvDnhp4mIUAAABtAAEBbR2Bo4ojlryZJqHKpSaiy6YnpM6oH4mtjxBX
+ bngAAABtAAAAbRVng30ik7iXJ6LLpiejzacnpM6oIZC1lBdtioAAAABtAAAAbQQrOHAdgqSKJJrCniek
+ zagnpM6oJJvCnh2CpIoEKzhwAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0DJjJvDU1idQ5PZXYWa4d/EVt0eQ9U
+ ancPUmh2CDtMcgAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0Wa4d/I5a9miagyKMpqtaxKq/btyqu
+ 2rcqrtq2Kq7atiqu2rYqrtq2KazXsyip064moMmjI5W8mRp4l4ULRVh0AAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAQFtF22KgCagyaQqrtq2Kq7atiqu
+ 2rYqrtq2Kq/btyy14sEtuejLLrroyy666MstuejLLLXiwSqv27cqrtq2Kq7atiqu2rYqrtq2JqLLpiSZ
+ wJwYc5GCEVlxeAAGC20AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0gjLGRKq7atiqu
+ 2rYqrtq2K7Pgvi2458grsd27KKnTriGRtpUhkbaVIZG2lSGRtpUhkbaVKarVsS256Movvu7WMMLz4C23
+ 5cYqrtq2Kq7atiqu2rYqrtq2Kq7ZtSekzqgjlbuYGnmZhQIfKW4AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbSCM
+ sZEqrtq2Kq7atiqu2rYrst+9LLXjwgtGWXQAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0EKjhvF26LgCmq1bAvv/DZMMHx2yux3rsqrtm1Kq7atiqu2rYqrtq2Kq7atht8nYcAAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtC0hcdCmr1rEqrtq2Kq7atiqu2rYhj7STAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0DIy9vHoWojCqv27cvwPDZL8Dw2i/A8NovwPDaKazXswUu
+ O3AAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtJZzEnyqu2rYqrtq2Kq7ath+Iq44AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbAAA
+ AGwAAABsAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABsLbflxiqu2rYqrtq2Kq7atgg6S3IAAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABsFWeCfRVngn0UZYB8DUtgdQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0MS191LbjmyCqu2rYqrtq2JqLLpgAA
+ AG0AAABtAAAAbQAAAG0AAABtIIuvkSms17Mpq9axKq7ZtSqu2rYqrtq2Kq3ZtSms2LMjl76aBjNCcQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABsLbflxiqu
+ 2rYqrtq2KKnTrgQnM28AAABtAAAAbQAAAG0AAABtAREYbhdvjIAmosumLLTiwS/A8NsrsNy5Kq7atiqu
+ 2rYqrtq2J6PMpwQqOG8AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtLbbkxSqu2rYqrtq2Kq7athRlf3wAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQ5R
+ Z3Yqrdi0LrrpzCqu2rYqrtq2Kq7atiGQtZQAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtIZG2lS2458kqrtq2Kq7atiejzKcGM0JxAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0BDRRtLLXiwiqu2rYqrtq2Kq7ath6Fp4wAAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAQ8WbS2558orsNy5Kq7atiqu2rYnosymF22KfwAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0UZYB8KazYsyqu2rYqrtq2KarVsRFYcHgAAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQ1LYHUuu+vPK7DcuSqu
+ 2rYqrtq2Kq7atiahyaQfia2PGneWhBp4l4Ube5uGHoWnjCagyaQprNezKq7atiqu2rcstePCGHGPgQMi
+ LW8BDBJtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0NS2B1KarVsC+97NMrst+8Kq7atiqu2rYqrtq2Kq7atiqu2rYqrtq2Kq7atiqu2rYqrtq3LbjnyC66
+ 6MsaeZmFAAAAbQpDVnMfiKuOEFdueAAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbRVog30stePDL77t1C6869Ett+XGLbflxi235cUstuTELrvqziy0
+ 4cAqr9u4HoepjQEPFW0AAABtAAAAbQAAAG0YcY+BKqzYtBp3loQABAZtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAyYybxVngn0km8KeJJvCniSb
+ wp4ikreVDUtgdQEWHm4AAABtDUxidRZsiX8AAABtAAAAbQAAAG0AAABtJ6TNqCuw3bolncWgCkFTcwAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtGneWhAAA
+ AG0AAABtAAAAbQEPFm4be5uGAAAAbQAAAG0AAABtAAAAbSOVu5gik7mXAh4pbgAAAG0AAABtDUtgdSmq
+ 1K8qrtq2Kq3ZtRdui4AAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtJJvDnhBWbngAAABtAAAAbQIbJG4stOHAG3ychwAAAG0AAABtAAAAbQ1NYnUqrtq2JqDJpAEW
+ Hm4AAABtAAAAbRdtioAstOHAKq7atiqt2bUkmsKeARYebgAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtJ6TNqCCNspIAAABtAAAAbQABAW0qrtq2J6PNpwYzQnEAAABtAAAAbQAA
+ AG0op9KsKq7ath2BoooAAABtAAAAbQAAAG0bfZ6HLLTiwCqu2rYqrtq2JqDJpAIeKW4AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtKazYsyio064DJjJvAAAAbQAAAG0jlr2aK7DcuSai
+ y6UAAABtAAAAbQAAAG0fiq2QLLXjwiqt2bUXbouAAAAAbQAAAG0AAABtG3ubhiy04sAqrtq2Kq7atiag
+ yaQEJjJvAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtJ6TOqSqu2rYdgaOKAAAAbQAA
+ AG0QWHB4LLbkwyqu2rUZdZSDAAAAbQAAAG0CFx9uLbjmxyqu2rcqrdm0F26LgAAAAG0AAABtAAAAbRt7
+ m4YstOLAKq7atiqu2rYmoMmkAh4pbgAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtJJvCniqu
+ 2rYmocmkAAAAbQAAAG0AAABtLbbkxSqu2rYop9GsARYebgAAAG0AAABtF26LgC256Msqrtq2Kq3ZtRdt
+ iX8AAABtAAAAbQAAAG0cfZ6HMMHx3Cuw3Lkqrtq2JqDJpAEWHm4AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtHoepjS2458kqrtq2DU1idQAAAG0AAABtIZG2lSyz4L4qrtq2IY+0lAAAAG0AAABtAAEBbSin
+ 0awqr9u3Kq7atiqt2bUQVm54AAAAbQAAAG0AAABtDk9ldiuy37wuuunMKq7ath2Co4oAAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAh0obi666cwqrtq2H4qukAAAAG0AAABtCUBScy245scqrtq2Kq3ZtRNh
+ e3sAAABtAAAAbQ9SaXcvvu7VKq3ZtSqu2rYlncWgARUdbgAAAG0AAABtAAAAbQIYIG4eh6mNJqDJpAQr
+ OXAAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbSy04cAqrtq2J6TOqAAAAG0AAABtAAAAbSqu
+ 2bUqr9u3Kq7atiWcw58AAABtAAAAbQAAAG0dg6WLLLXjwyqu2rYqrtq2JZzEnwAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbSekzqkqr9y4Kq3ZtQ1L
+ YHUAAABtAAAAbRNhe3stt+bHKq7atims17MUZH98AAAAbQAAAG0AAABtKq7atiuz4L4qrtq2KazXsxRl
+ gHwAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbRp5
+ mIUtuejLKq7ath+Iq44AAABtAAAAbQAAAG0rsd67Kq7atiqu2rYhj7SUAAAAbQAAAG0AAABtBCs4cCux
+ 3bsqrtq2Kq7atimr1rEAAABsAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAawAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0tt+XGKq/buB+JrI8AAABtAAAAbQAAAG0cfp+ILrzr0Cqu2rYpq9axDEpfdQAA
+ AG0AAABtAAAAbRNgensvvu3UKq7atiqu2rYehaiMAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABrAAAAXAAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0cf5+IL77u1RhykIIAAABtAAAAbQAAAG0ABgptLLXiwSqu
+ 2rYqrtq2HYGiigAAAG0AAABtAAAAbQAAAG0VZoJ9L77t1Cqu2rYmoMmjAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABcAAAAOwAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbAAAAG0AAABtAAAAbQAA
+ AG0AAABtEl54ei++7tUqrtq2I5W8mQAAAG0AAABtAAAAbQAAAG0AAABtFWeCfSyz4L8lnseiAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAAA7AAAADwAAAGgAAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbSOXvpostuPDI5a8mQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAGgAAAAPAAAAAAAA
+ ACsAAABrAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQEUHG4ik7iWGHKQggAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAawAA
+ ACsAAAAAAAAAAAAAAAAAAAArAAAAaAAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABoAAAAKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAADsAAABcAAAAawAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABrAAAAXAAAADsAAAAPAAAAAAAAAAAAAAAA4AAAAAAHAADAAAAAAAMAAIAAAAAAAQAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAABAADAAAAAAAMAAOAAAAAABwAAKAAAABAA
+ AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYAAABbAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAWwAAABYAAABbAAAAbSKTuZcdgqSKFmqGfiej
+ zKcYcI6BGXaVgyGPtJMXbYqAJZ7GoR+Hqo4WaoZ+IpK3lgAAAG0AAABbAAAAbQAAAG0gi6+QIpO4liOY
+ v5silLqYHH+giCKTuZcfiayPGXaVgySZwJwehaeMFmyIfyKSt5YAAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAh0mbhZsiX8dgqSKHYKkihZsiX8PVGt3AAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtGHKPgimr1rEnpc+pIpS6mCOXvpolnMSgK7LfvSin0qwkm8KeEFZueAAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbSelz6khkLWUAAAAbQAAAG0DISxvAREYbgAAAG0bfJyHIY+0lAxKX3UAAABtAAAAbQAA
+ AG0AAABtAAAAbQEQF24tuObHGHCNgQAAAG0bfJ2HKKfRrCux3rwSX3h6AAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtJJrBnSms2LMYcI6BEFVsdxVohH0prNeyHYGiigAAAG0AAABtAAAAbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQELEW0hj7SUKavWsSqv3LgkmcCcHH6fiBBVbHchkbaVAAIEbQAA
+ AG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbRyAoYkYc5GCEVlyeRhyj4EQVm54IIuvkSmp
+ 1K8KQlVzAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbQAAAG0lm8OfCkNWcyqs2LQGMkFwKKbRqw5P
+ ZHYhj7SUKq/btwpCVXMAAABtAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtJqDJpA9TaXckmsKdHIChiR+J
+ rI8pqdSvCkJUcxt6moYYcI6BAAAAbQAAAG0AAABtAAAAbQAAAG0AAABtAAAAbSKUuZcfiKuOGXSSgyel
+ z6oBFR1uKKbQqySZwJwAAABtAAAAbQAAAG0AAABtAAAAWwAAAG0AAABtAAAAbQAAAG0HN0hxEVpyeQAA
+ AG0pqtWwFGV/fAIYIW4lnMSgAAAAbQAAAG0AAABtAAAAWwAAABYAAABbAAAAbQAAAG0AAABtAAAAbQAA
+ AG0AAABtCD1OcgY0Q3EAAABtAAAAbQAAAG0AAABtAAAAWwAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiVBORw0KGgoAAAANSUhEUgAA
+ ALQAAAC0CAYAAAA9zQYyAAAeIUlEQVR4nOVdTesmx3H/9WNLbERkWeA1sliCDkYChWCxL4dAYkIuOeWU
+ L5HL7ofR98gpp9yCQzBhV4sdiInAELEsQkRIjiwhhAU7OczzMtNd7109z/zlgt35z0x3VXX3r2uqq2vm
+ Kcil2wDeOB5fB/AqgFsAXjqXKACmZKkibS7wBlDVJ+dTW1919Oi3AL4B8CWA3wH4FMAnx2MKlQQebwLl
+ p8D0FoDXEvitqaP39gzlHN0ELotbpz/noyL5Op32BYCPAPwWwMc9jHoA/Q6AdwHc6VFAJrp3rw9UhwYE
+ sCwsnUazT8ceMUzt5kp9gRf4HMBvAHwY1cRLbwH4WQH+TOyA66OujyL6b9DmTbvVOhnH0DMAv8Zsuc3k
+ AfQrAO4BeM8j4CbTTZ6TN1n3in4F4AMAX1sKf8/I9A6AnwN4O6hUmDKc/OvRzrUv4qmv8jh6A8CPAXwF
+ 4PdaYQug3wbwV0emDBXsYfByNSjEX9tJ3y9x7Qy0317lBwB+gjlC8plUUAP0uwD+8sgwTlcZ65FCI7xH
+ 6SPxtcosq7+3Gq5Si5YL38Jsrf8AIcwnAfptzGD+05UCuyBekyKc6bX3SPna9nB01XUh1iTvZcyewtdg
+ LDUH6DuY3YyAZe5yzGpjYZfTQwqr8QAYO8U2mcClOgaqGulloPwIwOcgfOrvExVeAfAAwA/9qgHN2rpe
+ ahcAkxC3XF4Wl+nVzSODQt+VSSlsjR0HWJ9LTU/uvd+wv//BI1N1lfsGNFVHIxki2JSwH2LG6Oeooh8H
+ ovQ9ZG2WUGvFBrWXy6JxZqfx8cZUznzI7igDLFUSUpZgLg9mEC/BfG0XaaR8GgkKzQrdwYzVFdWAfguW
+ ODPRwkK1eoUu3RWZFtenugzbTqNpmHhVTLQBqsr9Dx5Nj++9X+5/8GgG+WWSbqhGQ/RebQ911r8o9B5m
+ zJ6pBvTPnAwvl9SJpbgiRjlRWrl4FN/CnqTrsmJ7tM4nEK+Pdys3pIhqbAf23s6I1idbuMLsclH4DoC7
+ iYL66rnWlhsNZbIYGczz8aoKJtLATZvXMCc3fQasLfS7cXG2GeepV0iDzjS0jHgoj7XSFjAv3Q5RrxEK
+ RkhQzadZvaJS6YzdU5TjTTgWgpdURB95ytNlGQ6rqIksxa73NuCQwHxaIJJ6aQ1xhHvqCMvpyeAeY1dh
+ jbuL2R3MGP64HHn/HBPjP3+HslyuRXUXrqIaae6Gn6hQ4VKvOGcONMT106V+nP0awC9ml2NarxRX9B0F
+ M/lAC3srbUVLgEZ3N7rVYKmeVNQxTlyLGTBLVez0FjC7HLfBvmlSTRtqFgkzyzFPzXfjZddE1pr4x68s
+ 0x5NrX3nw/2ncQvdmH61xkqHpR4u+VlP7WkO9+oRMhO9BuD2AXPCBy9ROGWvKbdk/asJZC1rIpmhDuaA
+ zIVIaiH44sld1UKT+SlBUKW4O1NiNMXYBkoice2NA2YL7WDjLdFR07xpZOXKM1yC7XQ8X9PVcIkUF4IE
+ qNYsJq0pvCpGMOvuTp4fauV0Kqe4crcPmN/OJshmApodPY6aMh5LLMkv1Tkl2i6AGtyUp+sCJIeKf33e
+ 78MSAVIHmLdYkEZJGYvXD5g/NeCvyhWltsU1dl0bR5ZJpzg5ymAHtSNpdjPW/OvzxvWxzsdzuWmVuzIW
+ zPwz8grbPK8eMH83I4+oR6xQvKfR9eZLLy3jv+FoA5FGad1EWcpdsjQvmqY1pic4wfwgEl3hF8ibB8gK
+ bh2w/AjMprJn6ml0piuwGtzH68F2TTohDKX6zo/7nwhcvJtya5rJ+3h2f3rkxykhY2zCS1T6aJw8cdBU
+ wQZSdJPA1jXpXI/7p0PAxLk11ORNHxex37klnhVIrbYHHwMHf+LRuw0xAhmLea4lLNTCMpclDL5rm10X
+ Fmd3cwZPJtlyxQK7Eh1aBkmhOucOEL/X5p0R7L4cL5sY5KVF03kp+SMEeLoWZEqftmB+Sq4NQpPp3ChX
+ 6c2IcDmMoTqBIm3l99p6H4Kytx5b9RvTm47AosAzKppCW+a7tHtR+dIy58Zf7SAHQoqv+BrQAzyPhjpk
+ 2KsK1vh45MHWBzJyR8v42PfKqsnvZizaqQI007t27EFMmug1g8Pq+qiVmilTR2djV2+d4CCJzwbZ+bkS
+ AJdXFquDF8wMmQxIrxHswFwhGFwArTDO3v6Vy+Q6aNFst16Q5YDZ1hdyNKXy2Zl4dy3VhDWT9bS1IWO1
+ VAA8dPK5OSSMisWCRsX6QnX9ltkrr1funlPkzxb6aotWl2Cnlh1gzvBpXb6zL8sqJK/eXIm2qwfM5rB0
+ sMwZ0E3gTmMenAFsjlJhSxCFc5SJWTBOVjnzBAKWsna5nLnNFjn15oqlVRlkWUKxN30RUvJDMwm+U6Da
+ RJcwepAhXWKPY9tmgAyup4qcmZeWJdgV1WgkyhO1uRvZFTZZSq6yjfit7xFOEpeJJ5BLjbI6yEW97kbx
+ DYXM/67Jh+WyBAsSwbzMZmquXa43mkx1OQNt4Hjn5nJo5MzEc7s9i9z3whQMx58nfTxOvNlEoAE+bBjM
+ 0n4T01AppV0opVSKOjv03vIQQKf5Y1re6dReOlddPd5aRumr/9PTgfBVrxNFabfX10yYBkhyTXpR75Y4
+ ubpyKy5VTobsZoftnDGkrJBWLXarUN3WIbpZENSIUVNFXFSP8z3SLfTm4T9r31QJ91JIy8Jo7GZNVvZe
+ HMyEG72i6cm99+vJtTzyLtVYR/qQnea514A7Jnkh9UIbfGrxVBdJAxctI7oQtMlcNVIcx5pn7yTKNIKH
+ 82s7hlfTTaHiG0ChwT+OMNV060JzLscnylooI0THC9ffvVxOKk0fa5vCr+gRNw8Xho60Ua/fvzPSBkGC
+ nNj5JnDxibISeSaNC1TODZy5nXw+t24cDKBZuocG3ZccqZ+ksNNW/oVxHWEpZhl8D+R0N0DbRNFpq4Vg
+ AZpfC7G4Fz75hoF04mpZvHtRuImRtloRS5kUX9PD17aJAuh9aY1vz/L8IzNVW+75YB5PTkC3nWQBkbTj
+ yXZ7YFdRIsuCSubAbdUczzsHdxlH5fT2xbeNjzTKhy5jwHzeRB9oBZ2AjvkY06JqDW49t0Pe1PIEaaKg
+ IxeCsEcdDKpdXgww6N1Y6ia/2YgY5m2Q6bGy8BPeTdTbSG25OnIWFDrYefQIrRZVU3tdImmzVAJCXSEK
+ OmnRnPv4XfcHtxBsLHXzPY/44sY0SYl3E/u+5zGtDj10sPM4CaVAqOSFsZ846rD4pBZyhRE+nwnM5jzf
+ S8u8C8Go/nVbrPKao1NWIf7y123J7nKcXR8qZqd8PW54NETevTur4QWD0HMun9y0WmVUcIBZhUZne7go
+ jqF1rTziLxMVuYYd0FMN5ShKOxwldj0hZCktL3stm9JEl8X05A87wOVQV83HsHwVtZU/dVlbdy2lkaGw
+ nRnKpKYd5tqQwkmJEH1nrjcL/ffyS6Amn5xb0SqjGHYzmsiFTFIURYuqtE2TR4eLdmU+wEsBHqYxrHc2
+ RiVWkXx5YedBe7Be0BwCvt+KX6ZPe9zVWH7BaJzPPPdV2M14sP6OR0HRnM6lWO6UL+igeVFoStKgN4TF
+ VIAOMItGhuSrWAdqdR5XzwA2zQRX5+vwD/tp33L/g0d2C8wV7ADz/fajNCSYuVgnf9rc8eZxACeXo0nS
+ 4IXUVzygKIv/6XuStDhxPiFbXjkHtAWmwcIQeQgkyB4T/OunoCaE0B3w7Ty61wZKsht5t7oosmC69yAq
+ lUxSPHdkIITzCdnywrlk2XgOgm7H43mDyAGmKtLHEjdprDuPYn9ZdsbIuwZn2rK5UNHB2im7JONEzPZB
+ aX6W1SVPL5SoAlVHH7Z1PKrLzTAS5b1qkSlTDzURLrr1ByfbsWR51i+JCokT9XMXVBy/vs0jzWLamNQn
+ 7UZNF5gNEKG8V3V/QGfLR7gqnQ58XOkKFF1UKliq83eD2rGgiPJbjkaKhZzokzTLvDlE/Eb2QGtZxNOb
+ RqvvMavfQZYp1305riqSHvekhA3cjHFkwOY6TIeC3re+E2LNY8LV7SNXGySLHuZBdzZqb2C2qK+VGbUN
+ IVH/W98JGk8Y8RDwgXldg7nvAZulX5QsQAMHsD13So50WuTTMQRmynhuTP0WesVqqv7SSi9Ljp3P2QvC
+ DJ2W/EYvXFfHase0S17msCXwOixnldtKMtuE9uSyqbmSYaqbTZodgzn9Z5GL7pPLYHYOQKYNMrjM2v1E
+ C51L1/C/NMrWqWCOP7tALCpRwP0gEXWkc1nWAhRxwNTXL2l9utBll4DOoD1OiiXVFnKlb0D5ZQhx8yhG
+ AYo1G9LG7ry28qZXGABNsO1GS8tg7wBcUUDZZRUJVCprooAVzD6Ne+g6o2mMchCKic8hyx062enGUEBZ
+ C5hNrKnktkWUYg9gLpaMOaF+XDJroaUZVt/rNFde4upWm56siOONnl2+M4AIGdewTTNonz6antx9/0Bs
+ o4+TfOXnaiU+6EPHHaYBnoqZ+rapdWryHrTVVBKpvnL2yst0Ncyui7PN5Sjn/44U9/67+1VlsM6PWP5r
+ SlabCZfjU+Y6fSTlPZYmj68XxLRgwyZJ3typTOHxjzD7iG+1Ft6c2QA9rZm7PJwReSAiz6n5dvGqqsHX
+ 9IS+JJBLk0ltWrkc2NyrTX1lbhtwW3djnRDbanCjw3b1YEsgpgd/9jmpnTMtfutJjqf0MTdKof0lFI0n
+ KbxpzuXYPuGOf6ycaBk1qIHjssQOMBcNzNQ7gKAtN9nsAWDeZOwat3QcrXJ/iGjyw/Nf4eX6oqAUgUh+
+ OlEg9maVXePYdI0CRmoZZv2qaUbW3J6p1v1wvky1iFoDkDSRf3JFMuimgpmz2FwHk8Pi+ESvPa9m1ZsW
+ 1oGyCVI4V/54/VBdZslsqPuLyHo8Wf4swlODW7Hv47ldj22Lx3O9xaIxn7QsZ0PZRrH2+4ceG9euQaus
+ uuP1tF/BKithPLEbHac/hRFaDngkGiEehU/EbmmxNVCfJ8Ek9KeJommWbbqCoRgA06doYnpYNlbs+4RG
+ 57vTUWvBvIhSJFrKmKrrLDdvFITysW0uxZ69X5tu6pLLmaU0Azqa2uTVMkg0mHNBnEkj9Zu7Nt7Be54C
+ GZQch65mRtN7/u7MBnN/G3100/S10j4mRqtF3IcmnadpfST8KLYqxW0YmLeLqnP6DPmB+7pZ6c1cpBW4
+ FEkTWxEdn9nBTmE707LAnKtVnJoFqHMzJ0mNTcjab92pTkTR9N/6PgtyEd8ALRrh+XyWV62TVrTh47jR
+ 11f6rcDs+RFLvgVy22wtL9Vfhb6pkv6jxEWALRFM4ORTeeEYaKGjm4bLlf5Nt8w15bQnEofJblOAoyNS
+ Uf8AqJUWFlqaVjGfqHfTcAiYDeHXvYG59aX9GmZtjF3KBnrJWGUKsgdWgF7kYhAiuK3HbOr1nUXm1ay/
+ xio9D9QDBoLahVPL2smjcfSFgcPib5GTPf01p6O90YAlmB2L4jxSIgxLQFracyB864rjwAYkxzDK/G+4
+ AZkWgNYCavZGGPIAuEQcwnf2/RywroGimaifSLVQQQlLCO8FsQPq1qljS7u35FJ0meorHpKX4DVVUQ5e
+ VWtztaw8S85HbrzWtv6f7AbKIGV9prlRkUlroi19qspWrV68AIApqszU8JMoPWynCW7uE0Y7b7D1pbLd
+ lZJpIs6mJ+sMOr/vrHzX2mS2knxtjY0lWWqD/azvjxdB0BJnx+OLjlAdLyY7w4vQn7j1ggBfTwiSbaep
+ cdEeiKygFQOywRMjx0JrM6++TyXnIT7onNhh/UcwPlnjJZhPIFTbYUhdHdUUniK9Nx6xGtRyLLTbz6Dv
+ U4/Z0ZsoVqptj7ZIy7DIq/Z17ozsI5mon4Q9ZaBMDkBv0CPaAjD907MOotyIE3l9Yc/x3O29WYveBt+4
+ GTAtwnbbJZ+JpC0Aqfstl7zGnPgvJw911Cdh7CUEwJjvIBCTWaLXE9YIueT1V+Vb1S/JCgz0jBO/QnXJ
+ noWStjNk1GwNYs6y2i1u9M0VcyMUik4IbtOwO8bv0UWL8RK3iEWhkIHhmyw0P4FH14IwKbVgzf8uA97E
+ dxlHgbmIp4YbR0pwO8IsgvsBV8+HPgFzbQFtoNlCtzyw8u1aO62KAzvAv93WZR4nbReABuht716LZu62
+ YP9GQE3/DEQ2ZQDGkX7vFaeWj+vfCei8mZb+mL5xq/QjGfSO5pn/MVDnxkpu96X6nBtlBXKcw9zJRJ7S
+ FiGKhdftqXRdJQ4Z4n35UGTl+c+ghY6JHGfLpiD3S1cYU/fq9faEBDzZGKhBgFG4VvgeMvrAlw9FVp7/
+ 7HA7zPIKb+HGUE7irbm+iwkZuDQVV8WMshcGd2wXi8KZfL+zt2W0Y6nj1T1VJTnKGCu5Dhl1j9QHll8f
+ vZrvs/Q5JsLt8LwVTXJlLpi1qmgSCm3Uh8a9hquCmeuK3mQ8ZaGw+Pqos/m9Y0ft7E2U22G32Ev2TWuc
+ zYtlZnpjV/ulUh3r62rNwNa5rUfkPt6ZyzHTLnbaVrT9w3uWyMv1ahRaPG/qyh1loq+nL4AWORnFJAZI
+ uU0Ja27ELv3HDSkCYImuAe4IXeLQ4ugboSGkgXiJAumLCtwSqKV0z61oO6fiGIh8ov/q1gmY5+MD20/X
+ tTz36TJdAM3ody21NQusWeoTj9yW2LKzTn9OXAGVWxHO1jQD+K75dxib/lp+lkx4c4aQLGgVp2ZPwzls
+ qoU250I4Scv+iiX+0KCeB9s6ACJ8+BIT+SdRgN8+4ThQUjlLLIJXOxo+IMmq3EHLlp32NCzZwNQ4OBeF
+ ds80y4f1g5r/noXdD0zQfkACjgaonoX0/uL9ESqWXI52/liIL0k9ToSHr6vTudTMo05mCzMiCbhuo02G
+ 5hd3WeTA0dUHCV6eOUwIzPsYsFro0WEDJS9xxGDs0eJYQKO29/j96d7fesm30OtBTs86hdHl2EsIbKSl
+ kQZrZPutVs/dzsBH1bdyM0b25442Vo7NVFobG5STby372CttegaSaYP3kb2VG5EH4uubvhWgr6+ORrOG
+ u/ERM1t248CbTEn+x2pRKPEL+/fxkGJb6fhufc93LiyDWx+ziZJzDRDL7Surg7ltzR9siTVZnGND+XWU
+ Q1A+483dEA8yfWzKB4OyqRA+EjtxET0838numqR1ADia1LWqF4uU0YxprstrD/fvaug0xpLl/VLttdyI
+ Alx+r+S7MNASlQ0XhVv2ZQYoRoS89uwD32isL5QXAF01sW5xKYh/xDqPuIEYC9Jsy83xs37HY/v+vT4z
+ XkQD6HFyW842WX0aXdWCGuPB0mQLNzyh7/qpR76/bgHKQ1/iTuDNFtOLYsDqA+UOUZEuu7abwB2Hwa9O
+ ATTmmuTo08+lcRBW55ezHW2sXJ9ugs+7WuRZyvYIi9IVHwpHQAc12Fzxaz8+L+QG5yDVN+uRK3e9VTxj
+ ofcDnG6qVsA+5yqzH+iHZq8E1WIL92TZNxEDbPpom4weFxFgk5B2eCZxY4cXZFuoeq7VHKfqaqzRE8W6
+ FePnHv1t4jWTBB48x5b7pOVD9zdK7XCukqcvwv2mZW0bkLKq0NNfI/PPmNDmkK1hs4QgR/rvEwmAzp9d
+ LvJ8eXAAFtwsWX2zLB3NX+8lx6Sk+HXCgJRu5lmqo04HvnAgPGcoY1et9jezp9gY25HHtc5/oMej1/9W
+ +Q2AgZ3nVB11OoSjG5xsiSaftIuY2t/U1TEpM4DyuOZEf+u/2l7NpW32LnmKfR+6yqTqtUruZ0RV4aas
+ xXnHwVbXSxPxV65Nv4ZbWuu/dktkQBufH72AourT1kWowJcO0YihohwHq5xsh8atwEIL/jxJjItOC89Z
+ FxnQ0ZBPAtHWxVMvS34mleY0s3ViiHTkmtUgSt1l75C15G364c3uNl85YBIlv9otYNc0iae9JIZIRxkn
+ 3wOUJqKC78l1IeKHNwfQYP49gyTVlV0hQ43F6SgdvZQ+FIPGNhY86P7RoGsQtT8Up6buoKfVNbdcfBOC
+ KL2HJ6xxD8j/o0Fdjcvomczcik72owY6mW+3n76HMJJRh4NLVyVfpagDYVjUMGfnK50LCFaXvriYj4VW
+ 0DAoezCamZTVHp/LoXQ0+UZWeAe7ZRbKCxE5Wm+eyOVBJxRcyhSnY3Nfv95BA1iqXVLYk9WV8T50eoik
+ O/bQUTo/2UYn6/YvF8boCCEoomJUYjpM8h7n6Yod0Fd7xsWD+asagv6y7dMsI1VqezrLN6YgrOpsStPq
+ EKgpkh3QE64/aj0k9IbcUbaH+yTcc1OASWQncQ9rvWxaANqQ0dbxY9Lig/zJ/E057zGbrHKl32/JCLFN
+ j6P9kAzRY3akTY8ij4sEgERDuQD0OqONlhH3Ieu5sOyM6AunSz5ufYhB2VI+p0/kuyFUe04UHq8n997H
+ 5NHnrtwv0lxrM4XDxLocyg6qnxYVLx9RyXub+sQXMETFBrzNvZQ/k5heddZjmD6l6XZ9iDL0eXD5zkjb
+ Lwa0dj5kCjb6jEHWK/69fMLyC4DJ/lvkfAvmYP5W/SHpkMmHqkd9PEfXxEp0zQLXh2Zi5O4spZUeMCxZ
+ 9YLH3y66IXl62CZHfzt8lM3PQ24LHZ1RrsYRQji57GPv+PmtuuK1B2/UYEctrPbEscqvx6e/nQakUTgp
+ wMMIQD3AHjVTtU7L7+Sc9tWTjtM3S4+9Tq7e9lLky+VY0Loe7+xLjQuKvkhV3I5mi0QpX7fIsuD2to8D
+ M6Wv3gHx9kXKm9Va+NAS3xGObgHwjwBe6mczxlf0+MC9Fkm3GOsSpy8Kb2ERu5+IDz541KRrk3ocP+FL
+ lN+GHC1ti357APCNVN5GtAJ0Z9EW4HReH188oUJJtpCfrk+fhZTA7JW/Loim863JSKw+j636zD61OQUk
+ EjeWd+/sfNqi3xwAfJnAmoy6cp1VD7rX8loWMmN8xjbPI+sJ0YhRk0asRiRBH0liKEJQRqVRfFkA/C2A
+ Px/CHmM2DYYtWNjHLP8YFB/bDhBF21NrmOWG9epDa+etA2+9/yoA/gLA3zilmWgMSKXOkTsua5KM8uGt
+ 8m36tT9ncS19usk+H/71AOCTYXoYB93XedakAL8+QOXDF9q3j07OEHicj2bNvSP1IX5Kzid1MNmN9CcH
+ AJ8C+EIs1vPakwbmOGuPFgAC7o/zt7JP7eW0sPusixWJZ40UfWIQ7TR0J3uRNA6Lo40K8zdLXwD49JSc
+ 9JHIr+e1J80ixlmf+VPHqhQA4BQX7fkRy+gTphRP3Hci/uIYL/5MfGKc9ZGSv5mL9naSzSCEmFDy0ZLP
+ mwD+gS7XOjBeF9/9eDa6yXv1YbVGjNIjyjdbn15+wR3EfwLw8clCfwzgOaNetzDvjC3S2zESmB/wnVYC
+ etgbyCjKtGGEHj0gMutjyf5MmBw8vlgFnmPG8Cof+jd9wmSyD2Jh3+5euhWSz8vpzYG/20IHksfV/qjH
+ jhhLtT8kdyOijzL4Vj2s66bSnLAKnLG7BPSHAJ6NXKTpoJ6zv4DE6ALnY3oXQikdUJ1K7alfwyJeywr1
+ x5EoaLj0cY6PJlvVka/0DDN25zZUN98C8PdGeSxZ3RLroGT5eDY+J+1H5ILF9Orpj0grYnIvOSC1scjr
+ KZL+GYugBmWQ/xrAe4OVaGgUiL1yetpQg6dnSoigIfRm39/onJRZRkbUJK7mrwD8W82qplcA/B2AOyER
+ HtrOCP5xUXq/djIcM87PAfwLgK+XF79HFPwWwFco+AmAWxLH1mnfZpukFc7f21Cj8bR1H1+DSnWk6f8A
+ /DuAz+obFKAB4PeY00rfAPByXLvBZBnbrcY/TY6RUWn+iKkwKPEtzFOv+BWAXwL4H+omB2hgRv8fAPwY
+ DlDfFPvRp2fhmQzqgCFsl66AU8CVxvkrAP8B4L+5AhKggTnP42sAPwJwy92KnlbflJmxATUuqDCfqiId
+ ErN4ZTAAMLsZv4QAZkAHNDBb6s8BvArgB6eLvTreDLzuUEvTAmsjvc/uynB5zzH7zKSbsSQLoIHZp36G
+ eSPmDWBsEwybZOq99jrna+Y44loJWxsyerU3pLC3SVxOoblmAUiRFdDAHP14BuB/C/AnE/BaQLs+KkjB
+ 3/Aha8M/kYrE3Uwno1oN7iRAtaBnAH4B4D8xYy+TN0nvAHgXxnj1ViHnixyjxL3FbLekStUTGK6s/XPM
+ uRkfagUpypiXbwL4KeZt84vVrlfQkz7QUSiQi6aNRsUqSiuXpfKpq8sUyIpM0iFAX2Devv4tjllzUcp+
+ 0NzG7GPfBvA65oXkLQAvjdr7zKS1FqN1kvlv2iNV+spAS/0t5v2NLwH8DnMU7ZPjMYX+Hy0HAsXiH5HL
+ AAAAAElFTkSuQmCC
+
+
+
\ No newline at end of file
diff --git a/Epoc/Program.cs b/Epoc/Program.cs
new file mode 100644
index 0000000..daf2626
--- /dev/null
+++ b/Epoc/Program.cs
@@ -0,0 +1,31 @@
+using XWolf.Z;
+
+namespace Epoc
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main(string[] args)
+ {
+ if (args.Length > 0 && args[0] == "-repack")
+ Repack();
+ else
+ {
+ // To customize application configuration such as set high DPI settings or default font,
+ // see https://aka.ms/applicationconfiguration.
+ ApplicationConfiguration.Initialize();
+ Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
+ Application.Run(new Form1());
+ }
+ }
+
+ private static void Repack()
+ {
+ zPak z = new();
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Epoc/Properties/PublishProfiles/FolderProfile.pubxml b/Epoc/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 0000000..16e5022
--- /dev/null
+++ b/Epoc/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,18 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\publish\
+ FileSystem
+ <_TargetId>Folder
+ net9.0-windows
+ false
+ win-x86
+ true
+ true
+
+
\ No newline at end of file
diff --git a/Epoc/Properties/PublishProfiles/FolderProfile.pubxml.user b/Epoc/Properties/PublishProfiles/FolderProfile.pubxml.user
new file mode 100644
index 0000000..888326b
--- /dev/null
+++ b/Epoc/Properties/PublishProfiles/FolderProfile.pubxml.user
@@ -0,0 +1,10 @@
+
+
+
+
+ True|2025-02-12T23:00:51.0828684Z||;True|2025-02-12T23:50:49.9669136+01:00||;
+
+
+
\ No newline at end of file
diff --git a/Epoc/Properties/Resources.Designer.cs b/Epoc/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..5269118
--- /dev/null
+++ b/Epoc/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Este código fue generado por una herramienta.
+// Versión de runtime:4.0.30319.42000
+//
+// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si
+// se vuelve a generar el código.
+//
+//------------------------------------------------------------------------------
+
+namespace Epoc.Properties {
+ using System;
+
+
+ ///
+ /// Clase de recurso fuertemente tipado, para buscar cadenas traducidas, etc.
+ ///
+ // StronglyTypedResourceBuilder generó automáticamente esta clase
+ // a través de una herramienta como ResGen o Visual Studio.
+ // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen
+ // con la opción /str o recompile su proyecto de VS.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Epoc.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las
+ /// búsquedas de recursos mediante esta clase de recurso fuertemente tipado.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Epoc/Properties/Resources.resx b/Epoc/Properties/Resources.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Epoc/Properties/Resources.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Epoc/Properties/launchSettings.json b/Epoc/Properties/launchSettings.json
new file mode 100644
index 0000000..fdd893e
--- /dev/null
+++ b/Epoc/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Epoc": {
+ "commandName": "Project",
+ "commandLineArgs": "-h"
+ }
+ }
+}
\ No newline at end of file
diff --git a/res/Epoc-icon.ico b/res/Epoc-icon.ico
new file mode 100644
index 0000000..90b8d55
--- /dev/null
+++ b/res/Epoc-icon.ico
Binary files differ
diff --git a/res/Epoc-icon.png b/res/Epoc-icon.png
new file mode 100644
index 0000000..93fc52f
--- /dev/null
+++ b/res/Epoc-icon.png
Binary files differ
diff --git a/res/Epoc-logo.png b/res/Epoc-logo.png
new file mode 100644
index 0000000..7876986
--- /dev/null
+++ b/res/Epoc-logo.png
Binary files differ
diff --git a/zPakr/Cmds.cs b/zPakr/Cmds.cs
new file mode 100644
index 0000000..53c7df1
--- /dev/null
+++ b/zPakr/Cmds.cs
@@ -0,0 +1,59 @@
+namespace XWolf.Z
+{
+ internal class Cmds
+ {
+ public enum CmdAction { HELP, CREATE, ADD, LIST, EXTRACT, FILE, DUMP }
+ public Cmds(string[] args)
+ {
+ if (args.Length == 0)
+ return;
+ if (args[0].Length > 1 && args[0][0] != '-')
+ throw new ArgumentException("Parameters unknown.");
+ Action = args[0][1..].ToLower().Trim() switch
+ {
+ "c" => CmdAction.CREATE,
+ "a" => CmdAction.ADD,
+ "l" => CmdAction.LIST,
+ "x" => CmdAction.EXTRACT,
+ "f" => CmdAction.FILE,
+ "d" => CmdAction.DUMP,
+ "h" or "?" => CmdAction.HELP,
+ _ => throw new ArgumentException($"Unknown action {args[0]}."),
+ };
+ if (Action == CmdAction.HELP)
+ if (args.Length > 1)
+ throw new ArgumentException($"Too many parameters for action {args[0]}.");
+ else return;
+ PakFile = args[1];
+ switch (Action)
+ {
+ case CmdAction.CREATE:
+ case CmdAction.DUMP:
+ From = args.Length > 2 ? args[2] : throw new ArgumentException($"Missing parameters for action {args[0]}.");
+ if (args.Length > 3)
+ throw new ArgumentException($"Too many parameters for action {args[0]}.");
+ break;
+ case CmdAction.ADD:
+ case CmdAction.FILE:
+ From = args.Length > 2 ? args[2] : throw new ArgumentException($"Missing parameters for action {args[0]}.");
+ To = args.Length > 3 ? args[3] : string.Empty;
+ if (args.Length > 4)
+ throw new ArgumentException($"Too many parameters for action {args[0]}.");
+ break;
+ case CmdAction.LIST:
+ break;
+ case CmdAction.EXTRACT:
+ To = args.Length > 2 ? args[2] : string.Empty;
+ if (args.Length > 3)
+ throw new ArgumentException($"Too many parameters for action {args[0]}.");
+ break;
+
+ }
+ }
+
+ public CmdAction Action { get; set; } = CmdAction.HELP;
+ public string PakFile { get; set; } = string.Empty;
+ public string From { get; set; } = string.Empty;
+ public string To { get; set; } = string.Empty;
+ }
+}
diff --git a/zPakr/Program.cs b/zPakr/Program.cs
new file mode 100644
index 0000000..9b68cfc
--- /dev/null
+++ b/zPakr/Program.cs
@@ -0,0 +1,125 @@
+using XWolf.Z;
+
+Console.WriteLine("zPakr, a zPak file handler (C) XWolfOverride 2025. [https://github.com/XWolfOverride]");
+if (args.Length == 0)
+ DoHelp();
+else
+#if !DEBUG
+ try
+ {
+#else
+{
+#endif
+ var cmds = new Cmds(args);
+ switch (cmds.Action)
+ {
+ case Cmds.CmdAction.CREATE:
+ DoCreate(cmds.PakFile, cmds.From);
+ break;
+ case Cmds.CmdAction.ADD:
+ DoAdd(cmds.PakFile, cmds.From, cmds.To);
+ break;
+ case Cmds.CmdAction.LIST:
+ DoList(cmds.PakFile);
+ break;
+ case Cmds.CmdAction.EXTRACT:
+ DoExtract(cmds.PakFile, cmds.To);
+ break;
+ case Cmds.CmdAction.FILE:
+ DoFile(cmds.PakFile, cmds.From, cmds.To);
+ break;
+ case Cmds.CmdAction.DUMP:
+ DoDump(cmds.PakFile, cmds.From);
+ break;
+ case Cmds.CmdAction.HELP:
+ DoHelp();
+ break;
+ default:
+ throw new NotImplementedException();
+ }
+ }
+#if !DEBUG
+ catch (ArgumentException e)
+ {
+ Console.WriteLine($"[!! What?] {e.Message}");
+ DoHelp();
+ }
+ catch (NotImplementedException e)
+ {
+ Console.WriteLine($"[!! Where?] Something is missing! {e.Message}");
+ }
+ catch (IOException e)
+ {
+ Console.WriteLine($"[!! I/O?] {e.Message}");
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"[!! Huh?] {e.GetType().Name}:{e.Message}.");
+ }
+#endif
+
+static void DoHelp()
+{
+ Console.WriteLine(" usage:zPakr -c -l -x -f -d -h -?");
+ Console.WriteLine(" -c ...... Create pak-file from all source-folder files.");
+ //Console.WriteLine(" -a [] Adds source-file to pak-file, file name is used if no pak-path defined.");
+ Console.WriteLine(" -l ...................... List files inside pak-file.");
+ Console.WriteLine(" -x [] ...... Extract all files from pak-file to dest-folder or current one.");
+ Console.WriteLine(" -f [] . Extract single file from pak-file to dest-path or orignal file name in current folder.");
+ Console.WriteLine(" -d ............... Dump contents of file in pak-file to sdtout.");
+ Console.WriteLine(" -h or -? ........................... This help, default if no arguments.");
+ Console.WriteLine();
+ Console.WriteLine(" NOTE: Pak files maintains original folder structure, but does not record empty foldres.");
+ Console.WriteLine(" NOTE: Pak files are designed to work as stub on other files, -c can use any file with no pax-file data and appends a pak-file as stub maintaining original data.");
+}
+
+static void DoCreate(string pakFile, string source)
+{
+ if (File.Exists(pakFile))
+ File.Delete(pakFile);
+ var z = new zPak(pakFile);
+ //...
+ z.Close();
+}
+
+static void DoAdd(string pakFile, string source, string destPath)
+{
+ if (string.IsNullOrWhiteSpace(destPath))
+ destPath = Path.GetFileName(source);
+ var z = new zPak(pakFile, false);
+ //...
+ z.Close();
+ throw new NotImplementedException("Add command not supported right now.");
+}
+
+static void DoList(string pakFile)
+{
+ var z = new zPak(pakFile, false);
+ //...
+ z.Close();
+}
+
+static void DoExtract(string pakFile, string destFolder)
+{
+ if (string.IsNullOrWhiteSpace(destFolder))
+ destFolder = ".";
+ var z = new zPak(pakFile, false);
+ //...
+ z.Close();
+}
+
+static void DoFile(string pakFile, string file, string destPath)
+{
+ if (string.IsNullOrWhiteSpace(destPath))
+ destPath = Path.GetFileName(file);
+ var z = new zPak(pakFile, false);
+ //...
+ z.Close();
+}
+
+static void DoDump(string pakFile, string file)
+{
+ var z = new zPak(pakFile, false);
+ //...
+ z.Close();
+}
diff --git a/zPakr/Properties/launchSettings.json b/zPakr/Properties/launchSettings.json
new file mode 100644
index 0000000..0bd63aa
--- /dev/null
+++ b/zPakr/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "zPakr": {
+ "commandName": "Project",
+ "commandLineArgs": "-c sample.pak ."
+ }
+ }
+}
\ No newline at end of file
diff --git a/zPakr/zPak.cs b/zPakr/zPak.cs
new file mode 100644
index 0000000..d6fef43
--- /dev/null
+++ b/zPakr/zPak.cs
@@ -0,0 +1,240 @@
+using System.Text;
+
+namespace XWolf.Z
+{
+ public class zPak
+ {
+ private const string ZPAK_HEADER = "zPak!\u001a";
+ private const string ZPAK_FOOTER = "/zPak";
+ private class zPackEntry
+ {
+ public string Name { get; set; } = "";
+ public long EntryPosition { get; set; } = 0;
+ public long Position { get; set; } = 0;
+ public long Length { get; set; } = 0;
+ }
+
+ private readonly List entries = [];
+ private string? fileName;
+ private Stream? stream;
+ private long offset = -1;
+ private bool writting = false;
+
+ #region Constructors
+ public zPak()
+ {
+
+ }
+
+ public zPak(string name, bool autocreate = true)
+ {
+ Open(name, autocreate);
+ }
+
+ public zPak(Stream stream, bool autocreate = true)
+ {
+ Open(stream, autocreate);
+ }
+ #endregion
+
+ #region Stream / File access
+ public void Open(string name, bool autocreate = true)
+ {
+ fileName = name;
+ if (File.Exists(fileName))
+ OpenForRead(autocreate);
+ else
+ if (autocreate)
+ OpenForWrite();
+ else
+ throw new IOException("zPak does not exists.");
+ }
+
+ private void OpenForRead(bool autocreate)
+ {
+ if (fileName == null)
+ throw new Exception("Can't reopen stream");
+ Close();
+ writting = false;
+ var file = new FileStream(fileName, FileMode.Open, FileAccess.Read);
+ OpenStream(file, autocreate);
+ }
+
+ private void OpenForWrite()
+ {
+ if (fileName == null)
+ throw new Exception("Can't reopen stream");
+ Close();
+ writting = true;
+ var file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
+ OpenStream(file, true);
+ }
+
+ public void Open(Stream stream, bool autocreate = true)
+ {
+ fileName = null;
+ OpenStream(stream, autocreate);
+ }
+
+ private void OpenStream(Stream stream, bool autocreate)
+ {
+ this.stream = stream;
+ if (offset < 0)
+ {
+ ReadOffset(autocreate);
+ ReadDirectory();
+ }
+ }
+
+ public void Close()
+ {
+ if (stream == null)
+ return;
+ if (writting)
+ {
+ GetStream().Position = GetStream().Length;
+ WriteLong(offset);
+ WritePlainString(ZPAK_FOOTER);
+ }
+ if (stream != null)
+ {
+ stream.Close();
+ stream = null;
+ }
+ }
+ private Stream GetStream(bool forWrite = false)
+ {
+ if (stream == null)
+ throw new IOException("Pak not opened.");
+ if (forWrite && !stream.CanWrite)
+ OpenForWrite();
+ return stream;
+ }
+ #endregion
+
+ #region Stream I/O
+
+ private byte[] ReadBytes(int ctt)
+ {
+ byte[] buf = new byte[ctt];
+ if (GetStream().Read(buf, 0, ctt) != ctt)
+ throw new IOException("Unexpected end of file.");
+ return buf;
+ }
+
+ private int ReadInt()
+ {
+ return BitConverter.ToInt32(ReadBytes(4), 0);
+ }
+
+ private long ReadLong()
+ {
+ return BitConverter.ToInt64(ReadBytes(8), 0);
+ }
+
+ private string ReadPlainString(int len)
+ {
+ var strbuf = ReadBytes(len);
+ return Encoding.UTF8.GetString(strbuf, 0, strbuf.Length);
+ }
+
+ private string ReadString()
+ {
+ return ReadPlainString(ReadInt());
+ }
+
+ private void WriteBytes(byte[] bytes)
+ {
+ GetStream(true).Write(bytes);
+ }
+
+ private void WriteInt(int i)
+ {
+ WriteBytes(BitConverter.GetBytes(i));
+ }
+
+ private void WriteLong(long l)
+ {
+ WriteBytes(BitConverter.GetBytes(l));
+ }
+
+ private void WritePlainString(string str)
+ {
+ WriteBytes(Encoding.UTF8.GetBytes(str));
+ }
+
+ private void WriteString(string str)
+ {
+ WriteInt(str.Length);
+ WritePlainString(str);
+ }
+
+ #endregion
+
+ #region Pak header handling
+ private void ReadOffset(bool autocreate)
+ {
+ if (GetStream().Length > 0)
+ {
+ if (GetStream().Length > ZPAK_HEADER.Length + ZPAK_FOOTER.Length + 8)
+ {
+ GetStream().Position = GetStream().Length - ZPAK_FOOTER.Length;
+ if (ReadPlainString(8) == ZPAK_FOOTER)
+ {
+ GetStream().Position = GetStream().Length - (ZPAK_FOOTER.Length + 8);
+ offset = ReadLong();
+ }
+ else
+ PreparePakStub(autocreate);
+ }
+ else
+ PreparePakStub(autocreate);
+ }
+ else
+ PreparePakStub(autocreate);
+ }
+
+ private void PreparePakStub(bool autocreate)
+ {
+ if (!autocreate)
+ throw new IOException("Not a zPak.");
+ offset = GetStream().Position = GetStream().Length;
+ WritePlainString(ZPAK_HEADER);
+ }
+
+ private zPackEntry ReadEntry()
+ {
+ var entry = new zPackEntry
+ {
+ EntryPosition = ZPos,
+ Name = ReadString(),
+ Position = ZPos,
+ Length = ReadLong()
+ };
+ return entry;
+ }
+
+ private void ReadDirectory()
+ {
+ entries.Clear();
+ ZPos = 0;
+ if (ReadPlainString(ZPAK_HEADER.Length) != ZPAK_HEADER)
+ throw new Exception("Not a correct zPak file or corrupted");
+ var end = GetStream().Length - (ZPAK_FOOTER.Length + 8);
+ while (GetStream().Position < end)
+ {
+ var ent = ReadEntry();
+ GetStream().Seek(ent.Length, SeekOrigin.Current);
+ entries.Add(ent);
+ }
+ }
+
+ private long ZPos
+ {
+ get { return GetStream().Position - offset; }
+ set { GetStream().Position = value + offset; }
+ }
+ #endregion
+ }
+
+}
diff --git a/zPakr/zPakr.csproj b/zPakr/zPakr.csproj
new file mode 100644
index 0000000..39e3eae
--- /dev/null
+++ b/zPakr/zPakr.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ XWolf.Z
+
+
+