From 5c917be6be428e58b1e443c1f1a56b9dbf9afd47 Mon Sep 17 00:00:00 2001 From: Florian Geib Date: Tue, 2 Apr 2019 21:27:31 +0200 Subject: [PATCH] Version 2.0 - Python script RTD266xPy added - New operation mode selection window added - Allow to modify firmware image files - Instructions updated --- .gitignore | 3 +- README.md | 64 ++- RTD266xFlash/RTD266xFlash/App.config | 9 + .../BackgroundWorkers/ModifyFirmwareWorker.cs | 194 +------ RTD266xFlash/RTD266xFlash/FirmwareModifier.cs | 208 +++++++ .../{ => Forms}/FormAbout.Designer.cs | 6 +- .../RTD266xFlash/{ => Forms}/FormAbout.cs | 2 +- .../RTD266xFlash/{ => Forms}/FormAbout.resx | 0 .../FormArduino.Designer.cs} | 520 +---------------- .../{FormMain.cs => Forms/FormArduino.cs} | 238 +------- .../{FormFont.resx => Forms/FormArduino.resx} | 0 .../RTD266xFlash/Forms/FormExtras.Designer.cs | 96 ++++ RTD266xFlash/RTD266xFlash/Forms/FormExtras.cs | 86 +++ .../{FormMain.resx => Forms/FormExtras.resx} | 0 .../Forms/FormFileImage.Designer.cs | 129 +++++ .../RTD266xFlash/Forms/FormFileImage.cs | 141 +++++ .../RTD266xFlash/Forms/FormFileImage.resx | 120 ++++ .../{ => Forms}/FormFont.Designer.cs | 2 +- .../RTD266xFlash/{ => Forms}/FormFont.cs | 2 +- RTD266xFlash/RTD266xFlash/Forms/FormFont.resx | 120 ++++ .../RTD266xFlash/Forms/FormStart.Designer.cs | 171 ++++++ RTD266xFlash/RTD266xFlash/Forms/FormStart.cs | 57 ++ .../RTD266xFlash/Forms/FormStart.resx | 120 ++++ .../ModificationSettings.Designer.cs | 523 ++++++++++++++++++ .../RTD266xFlash/ModificationSettings.cs | 276 +++++++++ .../RTD266xFlash/ModificationSettings.resx | 120 ++++ RTD266xFlash/RTD266xFlash/Program.cs | 7 +- .../RTD266xFlash/Properties/AssemblyInfo.cs | 8 +- .../Properties/Settings.Designer.cs | 14 +- .../RTD266xFlash/Properties/Settings.settings | 5 +- RTD266xFlash/RTD266xFlash/RTD266xFlash.csproj | 60 +- RTD266xPy/rtd266x/__init__.py | 0 RTD266xPy/rtd266x/crc.py | 16 + RTD266xPy/rtd266x/rtd266x.py | 317 +++++++++++ RTD266xPy/rtd266x_flash.py | 119 ++++ screenshot.png | Bin 25474 -> 30339 bytes 36 files changed, 2815 insertions(+), 938 deletions(-) create mode 100644 RTD266xFlash/RTD266xFlash/FirmwareModifier.cs rename RTD266xFlash/RTD266xFlash/{ => Forms}/FormAbout.Designer.cs (96%) rename RTD266xFlash/RTD266xFlash/{ => Forms}/FormAbout.cs (95%) rename RTD266xFlash/RTD266xFlash/{ => Forms}/FormAbout.resx (100%) rename RTD266xFlash/RTD266xFlash/{FormMain.Designer.cs => Forms/FormArduino.Designer.cs} (52%) rename RTD266xFlash/RTD266xFlash/{FormMain.cs => Forms/FormArduino.cs} (64%) rename RTD266xFlash/RTD266xFlash/{FormFont.resx => Forms/FormArduino.resx} (100%) create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormExtras.Designer.cs create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormExtras.cs rename RTD266xFlash/RTD266xFlash/{FormMain.resx => Forms/FormExtras.resx} (100%) create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormFileImage.Designer.cs create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormFileImage.cs create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormFileImage.resx rename RTD266xFlash/RTD266xFlash/{ => Forms}/FormFont.Designer.cs (99%) rename RTD266xFlash/RTD266xFlash/{ => Forms}/FormFont.cs (99%) create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormFont.resx create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormStart.Designer.cs create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormStart.cs create mode 100644 RTD266xFlash/RTD266xFlash/Forms/FormStart.resx create mode 100644 RTD266xFlash/RTD266xFlash/ModificationSettings.Designer.cs create mode 100644 RTD266xFlash/RTD266xFlash/ModificationSettings.cs create mode 100644 RTD266xFlash/RTD266xFlash/ModificationSettings.resx create mode 100644 RTD266xPy/rtd266x/__init__.py create mode 100644 RTD266xPy/rtd266x/crc.py create mode 100644 RTD266xPy/rtd266x/rtd266x.py create mode 100644 RTD266xPy/rtd266x_flash.py diff --git a/.gitignore b/.gitignore index 6c4659e..89b59a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vs RTD266xFlash/RTD266xFlash/bin/ -RTD266xFlash/RTD266xFlash/obj/ \ No newline at end of file +RTD266xFlash/RTD266xFlash/obj/ +RTD266xPy/rtd266x/smbus.py \ No newline at end of file diff --git a/README.md b/README.md index 0e5f5ef..fff319f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,26 @@ # RTD266xFlash # -This is a combination of an Arduino project and a C# application to read and write the firmware of the Realtek RTD266x flat panel display controller. The Arduino code is based on [ladyada's project](https://github.com/adafruit/Adafruit_RTD266X_I2CFlasher). +This is a combination of an Arduino sketch, a Python script and a C# GUI application to read and write the firmware of the Realtek RTD266x flat panel display controller. The Arduino code is based on [ladyada's project](https://github.com/adafruit/Adafruit_RTD266X_I2CFlasher). -There are special features for a 3.5" HDMI display manufactured by KeDei: you can replace the boot logo with a custom logo, change the background and foreground colors and modify the "HDMI" pop-up. The custom logo needs to be 204x72 pixels and may only contain black and white pixels. +There are special features included for a 3.5" HDMI display manufactured by KeDei, which is equipped with an RTD2660: you can replace the boot logo with a custom logo, change the background and foreground colors and modify the "HDMI" pop-up. The custom logo needs to be 204x72 pixels and may only contain black and white pixels. ![Custom logo](custom_logo.png) ## Usage ## -### Arduino ### +In order to modify the RTD266x's firmware, you need to establish a connection to the chip. There are two ways to do that: -Compile the sketch with the Arduino IDE and download it onto your Arduino. You can close the Arduino IDE afterwards. +1. You can connect the display via HDMI to a Raspberry Pi, enable the I²C HDMI driver and read/write the firmware using the Python script without any additional hardware. The firmware will be stored in a file which you can open and modify with the C# GUI tool. The modified file is then written back to the display with the Python script. -### Connecting ### +2. You connect an Arduino to the display's I²C pins using a modified HDMI cable. The Arduino is connected to a PC/laptop and controlled with the C# GUI tool. + +If you don't have Microsoft Visual Studio to compile the C# GUI tool, you can download the EXE file from the [releases](https://github.com/floppes/RTD266xFlash/releases). + +### Method 1: Arduino ### + +Compile the RTD266xArduino sketch with the Arduino IDE and download it onto your Arduino. You can close the Arduino IDE afterwards. + +#### Connecting #### The communication between display and Arduino is done via I²C which is accessible on the VGA and HDMI ports of RTD266x. Connect SCL, SDA and GND with the corresponding Arduino pins. @@ -32,15 +40,51 @@ A flashing user LED on the Arduino indicates a problem with the connection. Rese ![Setup example](setup.png) -### GUI tool ### - -If you don't have Microsoft Visual Studio to compile the C# tool, you can download the EXE file from the [releases](https://github.com/floppes/RTD266xFlash/releases). +#### GUI tool #### -Select the COM port your Arduino is connected to and click **Connect**. If there was an error it will tell you what went wrong. You can click **Read status** to check the connection and read some info about the flash chip. It should return values different from 0x00 and 0xFF. If it doesn't, try again or reset the Arduino. +Start RTD266xFlash.exe and select **Connect directly with an Arduino**. Select the COM port your Arduino is connected to and click **Connect**. If there was an error it will tell you what went wrong. You can click **Read status** to check the connection and read some info about the flash chip. It should return values different from 0x00 and 0xFF. If it doesn't, try again or reset the Arduino. ![Screenshot of GUI tool](screenshot.png) -## Firmware modifications ## +### Method 2: Firmware images with Python script ### + +For this method you need a Raspberry Pi running a current version of Raspbian or any derivative. Use a standard HDMI cable to connect the display to the Raspberry Pi. In your `/boot/config.txt` file you need to add the following line to enable the I²C on HDMI interface: + +`dtparam=i2c2_iknowwhatimdoing` + +You will also need to install the I²C Python library with this command: + +`sudo apt-get install python-smbus` + +After a reboot, enable the I²C driver by executing: + +`sudo raspi-config` + +Navigate to `Interfacing Options`, `I2C` and select `Yes`. Use `Back` and `Finish` to leave the configuration tool. + +You can then scan for I²C devices with this command: + +`sudo i2cdetect -y 2` + +It should find a device at address `4a`. + +To download the Python scripts, run the following commands: + +1. `cd ~` +2. `git clone https://github.com/floppes/RTD266xFlash.git` +3. `cd RTD266xFlash/RTD266xPy` + +You are now ready to read the display's firmware with this command: + +`python rtd266x_flash.py -r 524288 out.bin` + +The number is the firmware's size in bytes (512 x 1024 = 512 KB). This will take about 2 minutes. Transfer the file `out.bin` to your PC/laptop where you have the GUI tool RTD266xFlash.exe. Start it and select **Firmware images**. Select `out.bin` as the input file and configure the modifications you want to perform. Click **Modify firmware** and save the modified firmware file. Transfer the modified firmware file to the Raspberry Pi and run + +`python rtd266x_flash.py -d out.bin out_modified.bin` + +where `out_modified.bin` is the modified firmware file. This will write all modified sectors of file `out_modified.bin` to the display, skipping the unmodified sectors to speed things up. Enjoy your modified firmware! + +## Expert knowledge: details of firmware modifications ## **Attention:** Before you modify your firmware, create a backup of the original firmware! diff --git a/RTD266xFlash/RTD266xFlash/App.config b/RTD266xFlash/RTD266xFlash/App.config index dac0986..7ab85a5 100644 --- a/RTD266xFlash/RTD266xFlash/App.config +++ b/RTD266xFlash/RTD266xFlash/App.config @@ -2,6 +2,7 @@ +
@@ -9,6 +10,14 @@ + + + False + + + False + + False diff --git a/RTD266xFlash/RTD266xFlash/BackgroundWorkers/ModifyFirmwareWorker.cs b/RTD266xFlash/RTD266xFlash/BackgroundWorkers/ModifyFirmwareWorker.cs index 8592842..5978711 100644 --- a/RTD266xFlash/RTD266xFlash/BackgroundWorkers/ModifyFirmwareWorker.cs +++ b/RTD266xFlash/RTD266xFlash/BackgroundWorkers/ModifyFirmwareWorker.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; @@ -23,78 +22,6 @@ public class ModifyFirmwareWorker : BaseWorker public delegate void ModifyFirmwareWorkerFinishedEvent(RTD266x.Result result); public event ModifyFirmwareWorkerFinishedEvent ModifyFirmwareWorkerFinished; - /// - /// Character mapping to internal font - /// - private readonly Dictionary _osdCharacters = new Dictionary - { - { ' ', new byte[] { 0x01 }}, - { 'A', new byte[] { 0x10, 0x11 }}, - { 'B', new byte[] { 0x12 }}, - { 'C', new byte[] { 0x13 }}, - { 'D', new byte[] { 0x14 }}, - { 'E', new byte[] { 0x15 }}, - { 'F', new byte[] { 0x16 }}, - { 'G', new byte[] { 0x17, 0x18 }}, - { 'H', new byte[] { 0x19 }}, - { 'I', new byte[] { 0x1A }}, - { 'J', new byte[] { 0x1B }}, - { 'K', new byte[] { 0x1C }}, - { 'L', new byte[] { 0x1D }}, - { 'M', new byte[] { 0x1E, 0x1F }}, - { 'N', new byte[] { 0x20 }}, - { 'O', new byte[] { 0x21, 0x22 }}, - { 'P', new byte[] { 0x23 }}, - { 'Q', new byte[] { 0x24, 0x25 }}, - { 'R', new byte[] { 0x26 }}, - { 'S', new byte[] { 0x27 }}, - { 'T', new byte[] { 0x28 }}, - { 'U', new byte[] { 0x29 }}, - { 'V', new byte[] { 0x2A }}, - { 'W', new byte[] { 0x2B, 0x2C }}, - { 'X', new byte[] { 0x2D }}, - { 'Y', new byte[] { 0x2E }}, - { 'Z', new byte[] { 0x2F }}, - { '0', new byte[] { 0x30 }}, - { '1', new byte[] { 0x31 }}, - { '2', new byte[] { 0x32 }}, - { '3', new byte[] { 0x33 }}, - { '4', new byte[] { 0x34 }}, - { '5', new byte[] { 0x35 }}, - { '6', new byte[] { 0x36 }}, - { '7', new byte[] { 0x37 }}, - { '8', new byte[] { 0x38 }}, - { '9', new byte[] { 0x39 }}, - { 'a', new byte[] { 0x3A }}, - { 'b', new byte[] { 0x3B }}, - { 'c', new byte[] { 0x3C }}, - { 'd', new byte[] { 0x3D }}, - { 'e', new byte[] { 0x3E }}, - { 'f', new byte[] { 0x3F }}, - { 'g', new byte[] { 0x40 }}, - { 'h', new byte[] { 0x41 }}, - { 'i', new byte[] { 0x42 }}, - { 'j', new byte[] { 0x43 }}, - { 'k', new byte[] { 0x44 }}, - { 'l', new byte[] { 0x45 }}, - { 'm', new byte[] { 0x46, 0x47 }}, - { 'n', new byte[] { 0x48 }}, - { 'o', new byte[] { 0x49 }}, - { 'p', new byte[] { 0x4A }}, - { 'q', new byte[] { 0x4B }}, - { 'r', new byte[] { 0x4C }}, - { 's', new byte[] { 0x4D }}, - { 't', new byte[] { 0x4E }}, - { 'u', new byte[] { 0x4F }}, - { 'v', new byte[] { 0x50 }}, - { 'w', new byte[] { 0x51, 0x52 }}, - { 'x', new byte[] { 0x53 }}, - { 'y', new byte[] { 0x54 }}, - { 'z', new byte[] { 0x55 }}, - { ':', new byte[] { 0x5E }}, - { '.', new byte[] { 0x5F }}, - }; - public ModifyFirmwareWorker(RTD266x rtd, string logoFileName, Color logoBackground, Color logoForeground, Color displayBackground, bool removeHdmi, string replaceHdmi) : base(rtd) { _logoFileName = logoFileName; @@ -107,12 +34,12 @@ public ModifyFirmwareWorker(RTD266x rtd, string logoFileName, Color logoBackgrou protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { + string error; + if (!string.IsNullOrEmpty(_logoFileName)) { ReportStatus("Checking logo file... "); - string error; - if (!FontCoder.CheckFile(_logoFileName, FontCoder.FontWidthKedei, FontCoder.FontHeightKedei, out error)) { ReportStatus($"Error! {error}\r\n"); @@ -127,23 +54,13 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs { ReportStatus("Checking \"HDMI\" replacement... "); - if (_replaceHdmi.Length > 8) + if (!FirmwareModifier.CheckHdmiReplacement(_replaceHdmi, out error)) { - ReportStatus("Error! String is too long!\r\n"); + ReportStatus($"{error}\r\n"); e.Result = RTD266x.Result.NotOk; return; } - - foreach (char chr in _replaceHdmi) - { - if (!_osdCharacters.ContainsKey(chr)) - { - ReportStatus($"Error! Invalid character \"{chr}\"!\r\n"); - e.Result = RTD266x.Result.NotOk; - return; - } - } - + ReportStatus("ok\r\n"); } @@ -175,7 +92,7 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs return; } - string backupFirmwareFileName = "firmware-" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".bin"; + string backupFirmwareFileName = "firmware-" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".bin"; ReportStatus($"Creating firmware backup file \"{backupFirmwareFileName}\"... "); @@ -193,20 +110,11 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs ReportStatus("ok\r\n"); ReportStatus("Checking firmware... "); - Firmware detectedFirmware = null; - - foreach (Firmware fw in Firmware.KnownFirmwares) - { - if (fw.CheckHash(firmware)) - { - detectedFirmware = fw; - break; - } - } + Firmware detectedFirmware = FirmwareModifier.DetectFirmware(firmware); if (detectedFirmware == null) { - ReportStatus("Error! Unknown firmware. You can send your firmware to the author, maybe it can be added to the known firmwares.\r\n"); + ReportStatus("Error! Unknown firmware. You can send your firmware to the author (floppes@gmx.de), maybe it can be added to the known firmwares.\r\n"); e.Result = result; return; } @@ -216,31 +124,15 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs if (!string.IsNullOrEmpty(_logoFileName)) { - ReportStatus("Converting logo... "); - - FontCoder logo = new FontCoder(FontCoder.FontWidthKedei, FontCoder.FontHeightKedei); - - if (!logo.LoadImage(_logoFileName)) - { - ReportStatus($"Error! Cannot load logo from \"{_logoFileName}\".\r\n"); - e.Result = result; - return; - } - - byte[] logoBytes = logo.Encode(); + ReportStatus("Converting and embedding the new logo... "); - if (logoBytes.Length > detectedFirmware.MaxLogoLength) + if (!FirmwareModifier.ChangeLogo(_logoFileName, firmware, detectedFirmware, out error)) { - ReportStatus("Error! Encoded logo is too long and would overwrite other firmware parts.\r\n"); + ReportStatus($"{error}\r\n"); e.Result = result; return; } - ReportStatus("ok\r\n"); - ReportStatus("Embedding the new logo... "); - - Array.Copy(logoBytes, 0, firmware, detectedFirmware.LogoOffset, logoBytes.Length); - ReportStatus("ok\r\n"); result = WritePatchedSector(firmware, detectedFirmware.LogoOffset); @@ -256,9 +148,7 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs { ReportStatus("Patching logo background color... "); - firmware[detectedFirmware.PaletteOffset + 42] = _logoBackground.R; - firmware[detectedFirmware.PaletteOffset + 43] = _logoBackground.G; - firmware[detectedFirmware.PaletteOffset + 44] = _logoBackground.B; + FirmwareModifier.ChangeLogoBackgroundColor(_logoBackground, firmware, detectedFirmware); ReportStatus("ok\r\n"); } @@ -267,9 +157,7 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs { ReportStatus("Patching logo foreground color... "); - firmware[detectedFirmware.PaletteOffset + 12] = _logoForeground.R; - firmware[detectedFirmware.PaletteOffset + 13] = _logoForeground.G; - firmware[detectedFirmware.PaletteOffset + 14] = _logoForeground.B; + FirmwareModifier.ChangeLogoForegroundColor(_logoForeground, firmware, detectedFirmware); ReportStatus("ok\r\n"); } @@ -289,22 +177,7 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs { ReportStatus("Patching display background color... "); - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x1D] = 0x7D; // MOV R5 - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x1E] = _displayBackground.R; - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x1F] = 0x00; // NOP - - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x23] = 0x7D; // MOV R5 - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x24] = _displayBackground.G; - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x25] = 0x00; // NOP - - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x29] = 0x7D; // MOV R5 - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2A] = _displayBackground.B; - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2B] = 0x00; // NOP - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2C] = 0x00; // NOP - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2D] = 0x00; // NOP - - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x3C] = 0x00; // NOP - firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x3D] = 0x00; // NOP + FirmwareModifier.ChangeBackgroundColor(_displayBackground, firmware, detectedFirmware); ReportStatus("ok\r\n"); @@ -321,53 +194,32 @@ protected override void _backgroundWorker_DoWork(object sender, DoWorkEventArgs { ReportStatus("Removing \"HDMI\" pop-up... "); - firmware[detectedFirmware.ShowNoteOffset] = 0x22; // RET + FirmwareModifier.ToggleHdmiPopup(false, firmware, detectedFirmware); ReportStatus("ok\r\n"); - - result = WritePatchedSector(firmware, detectedFirmware.ShowNoteOffset); - - if (result != RTD266x.Result.Ok) - { - e.Result = result; - return; - } } else { ReportStatus("Enabling \"HDMI\" pop-up... "); - firmware[detectedFirmware.ShowNoteOffset] = 0xE4; // CLR A + FirmwareModifier.ToggleHdmiPopup(true, firmware, detectedFirmware); ReportStatus("ok\r\n"); + } - result = WritePatchedSector(firmware, detectedFirmware.ShowNoteOffset); + result = WritePatchedSector(firmware, detectedFirmware.ShowNoteOffset); - if (result != RTD266x.Result.Ok) - { - e.Result = result; - return; - } + if (result != RTD266x.Result.Ok) + { + e.Result = result; + return; } if (!string.IsNullOrEmpty(_replaceHdmi)) { ReportStatus($"Replacing \"HDMI\" pop-up with \"{_replaceHdmi}\"... "); - int offset = 0; - - foreach (char chr in _replaceHdmi) - { - byte[] bytes = _osdCharacters[chr]; - - foreach (byte b in bytes) - { - firmware[detectedFirmware.HdmiStringOffset + offset] = b; - offset++; - } - } - - firmware[detectedFirmware.HdmiStringOffset + offset] = 0x00; + FirmwareModifier.ReplaceHdmiPopup(_replaceHdmi, firmware, detectedFirmware); ReportStatus("ok\r\n"); diff --git a/RTD266xFlash/RTD266xFlash/FirmwareModifier.cs b/RTD266xFlash/RTD266xFlash/FirmwareModifier.cs new file mode 100644 index 0000000..765f481 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/FirmwareModifier.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace RTD266xFlash +{ + public static class FirmwareModifier + { + /// + /// Character mapping to internal font + /// + private static readonly Dictionary _osdCharacters = new Dictionary + { + { ' ', new byte[] { 0x01 }}, + { 'A', new byte[] { 0x10, 0x11 }}, + { 'B', new byte[] { 0x12 }}, + { 'C', new byte[] { 0x13 }}, + { 'D', new byte[] { 0x14 }}, + { 'E', new byte[] { 0x15 }}, + { 'F', new byte[] { 0x16 }}, + { 'G', new byte[] { 0x17, 0x18 }}, + { 'H', new byte[] { 0x19 }}, + { 'I', new byte[] { 0x1A }}, + { 'J', new byte[] { 0x1B }}, + { 'K', new byte[] { 0x1C }}, + { 'L', new byte[] { 0x1D }}, + { 'M', new byte[] { 0x1E, 0x1F }}, + { 'N', new byte[] { 0x20 }}, + { 'O', new byte[] { 0x21, 0x22 }}, + { 'P', new byte[] { 0x23 }}, + { 'Q', new byte[] { 0x24, 0x25 }}, + { 'R', new byte[] { 0x26 }}, + { 'S', new byte[] { 0x27 }}, + { 'T', new byte[] { 0x28 }}, + { 'U', new byte[] { 0x29 }}, + { 'V', new byte[] { 0x2A }}, + { 'W', new byte[] { 0x2B, 0x2C }}, + { 'X', new byte[] { 0x2D }}, + { 'Y', new byte[] { 0x2E }}, + { 'Z', new byte[] { 0x2F }}, + { '0', new byte[] { 0x30 }}, + { '1', new byte[] { 0x31 }}, + { '2', new byte[] { 0x32 }}, + { '3', new byte[] { 0x33 }}, + { '4', new byte[] { 0x34 }}, + { '5', new byte[] { 0x35 }}, + { '6', new byte[] { 0x36 }}, + { '7', new byte[] { 0x37 }}, + { '8', new byte[] { 0x38 }}, + { '9', new byte[] { 0x39 }}, + { 'a', new byte[] { 0x3A }}, + { 'b', new byte[] { 0x3B }}, + { 'c', new byte[] { 0x3C }}, + { 'd', new byte[] { 0x3D }}, + { 'e', new byte[] { 0x3E }}, + { 'f', new byte[] { 0x3F }}, + { 'g', new byte[] { 0x40 }}, + { 'h', new byte[] { 0x41 }}, + { 'i', new byte[] { 0x42 }}, + { 'j', new byte[] { 0x43 }}, + { 'k', new byte[] { 0x44 }}, + { 'l', new byte[] { 0x45 }}, + { 'm', new byte[] { 0x46, 0x47 }}, + { 'n', new byte[] { 0x48 }}, + { 'o', new byte[] { 0x49 }}, + { 'p', new byte[] { 0x4A }}, + { 'q', new byte[] { 0x4B }}, + { 'r', new byte[] { 0x4C }}, + { 's', new byte[] { 0x4D }}, + { 't', new byte[] { 0x4E }}, + { 'u', new byte[] { 0x4F }}, + { 'v', new byte[] { 0x50 }}, + { 'w', new byte[] { 0x51, 0x52 }}, + { 'x', new byte[] { 0x53 }}, + { 'y', new byte[] { 0x54 }}, + { 'z', new byte[] { 0x55 }}, + { ':', new byte[] { 0x5E }}, + { '.', new byte[] { 0x5F }}, + }; + + public static Firmware DetectFirmware(byte[] firmware) + { + Firmware detectedFirmware = null; + + foreach (Firmware fw in Firmware.KnownFirmwares) + { + if (fw.CheckHash(firmware)) + { + detectedFirmware = fw; + break; + } + } + + return detectedFirmware; + } + + public static bool ChangeLogo(string logoFileName, byte[] firmware, Firmware detectedFirmware, out string error) + { + error = string.Empty; + + FontCoder logo = new FontCoder(FontCoder.FontWidthKedei, FontCoder.FontHeightKedei); + + if (!logo.LoadImage(logoFileName)) + { + error = $"Error! Cannot load logo from \"{logoFileName}\"."; + return false; + } + + byte[] logoBytes = logo.Encode(); + + if (logoBytes.Length > detectedFirmware.MaxLogoLength) + { + error = "Error! Encoded logo is too long and would overwrite other firmware parts."; + return false; + } + + Array.Copy(logoBytes, 0, firmware, detectedFirmware.LogoOffset, logoBytes.Length); + + return true; + } + + public static void ChangeLogoBackgroundColor(Color color, byte[] firmware, Firmware detectedFirmware) + { + firmware[detectedFirmware.PaletteOffset + 42] = color.R; + firmware[detectedFirmware.PaletteOffset + 43] = color.G; + firmware[detectedFirmware.PaletteOffset + 44] = color.B; + } + + public static void ChangeLogoForegroundColor(Color color, byte[] firmware, Firmware detectedFirmware) + { + firmware[detectedFirmware.PaletteOffset + 12] = color.R; + firmware[detectedFirmware.PaletteOffset + 13] = color.G; + firmware[detectedFirmware.PaletteOffset + 14] = color.B; + } + + public static void ChangeBackgroundColor(Color color, byte[] firmware, Firmware detectedFirmware) + { + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x1D] = 0x7D; // MOV R5 + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x1E] = color.R; + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x1F] = 0x00; // NOP + + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x23] = 0x7D; // MOV R5 + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x24] = color.G; + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x25] = 0x00; // NOP + + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x29] = 0x7D; // MOV R5 + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2A] = color.B; + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2B] = 0x00; // NOP + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2C] = 0x00; // NOP + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x2D] = 0x00; // NOP + + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x3C] = 0x00; // NOP + firmware[detectedFirmware.AdjustBackgroundColorOffset + 0x3D] = 0x00; // NOP + } + + public static bool CheckHdmiReplacement(string hdmiReplacement, out string error) + { + error = string.Empty; + + if (hdmiReplacement.Length > 8) + { + error = "Error! String is too long!"; + return false; + } + + foreach (char chr in hdmiReplacement) + { + if (!_osdCharacters.ContainsKey(chr)) + { + error = $"Error! Invalid character \"{chr}\"!"; + return false; + } + } + + return true; + } + + public static void ToggleHdmiPopup(bool enabled, byte[] firmware, Firmware detectedFirmware) + { + if (enabled) + { + firmware[detectedFirmware.ShowNoteOffset] = 0xE4; // CLR A + } + else + { + firmware[detectedFirmware.ShowNoteOffset] = 0x22; // RET + } + } + + public static void ReplaceHdmiPopup(string hdmiReplacement, byte[] firmware, Firmware detectedFirmware) + { + int offset = 0; + + foreach (char chr in hdmiReplacement) + { + byte[] bytes = _osdCharacters[chr]; + + foreach (byte b in bytes) + { + firmware[detectedFirmware.HdmiStringOffset + offset] = b; + offset++; + } + } + + firmware[detectedFirmware.HdmiStringOffset + offset] = 0x00; + } + } +} diff --git a/RTD266xFlash/RTD266xFlash/FormAbout.Designer.cs b/RTD266xFlash/RTD266xFlash/Forms/FormAbout.Designer.cs similarity index 96% rename from RTD266xFlash/RTD266xFlash/FormAbout.Designer.cs rename to RTD266xFlash/RTD266xFlash/Forms/FormAbout.Designer.cs index e6494ba..0ada92d 100644 --- a/RTD266xFlash/RTD266xFlash/FormAbout.Designer.cs +++ b/RTD266xFlash/RTD266xFlash/Forms/FormAbout.Designer.cs @@ -1,4 +1,4 @@ -namespace RTD266xFlash +namespace RTD266xFlash.Forms { partial class FormAbout { @@ -48,9 +48,9 @@ private void InitializeComponent() this.lblAuthor.AutoSize = true; this.lblAuthor.Location = new System.Drawing.Point(12, 31); this.lblAuthor.Name = "lblAuthor"; - this.lblAuthor.Size = new System.Drawing.Size(61, 13); + this.lblAuthor.Size = new System.Drawing.Size(157, 13); this.lblAuthor.TabIndex = 1; - this.lblAuthor.Text = "by floppes"; + this.lblAuthor.Text = "by floppes (floppes@gmx.de)"; // // lblLink // diff --git a/RTD266xFlash/RTD266xFlash/FormAbout.cs b/RTD266xFlash/RTD266xFlash/Forms/FormAbout.cs similarity index 95% rename from RTD266xFlash/RTD266xFlash/FormAbout.cs rename to RTD266xFlash/RTD266xFlash/Forms/FormAbout.cs index a6ab328..945c524 100644 --- a/RTD266xFlash/RTD266xFlash/FormAbout.cs +++ b/RTD266xFlash/RTD266xFlash/Forms/FormAbout.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using System.Windows.Forms; -namespace RTD266xFlash +namespace RTD266xFlash.Forms { public partial class FormAbout : Form { diff --git a/RTD266xFlash/RTD266xFlash/FormAbout.resx b/RTD266xFlash/RTD266xFlash/Forms/FormAbout.resx similarity index 100% rename from RTD266xFlash/RTD266xFlash/FormAbout.resx rename to RTD266xFlash/RTD266xFlash/Forms/FormAbout.resx diff --git a/RTD266xFlash/RTD266xFlash/FormMain.Designer.cs b/RTD266xFlash/RTD266xFlash/Forms/FormArduino.Designer.cs similarity index 52% rename from RTD266xFlash/RTD266xFlash/FormMain.Designer.cs rename to RTD266xFlash/RTD266xFlash/Forms/FormArduino.Designer.cs index ae0f5f7..efc38ec 100644 --- a/RTD266xFlash/RTD266xFlash/FormMain.Designer.cs +++ b/RTD266xFlash/RTD266xFlash/Forms/FormArduino.Designer.cs @@ -1,6 +1,6 @@ -namespace RTD266xFlash +namespace RTD266xFlash.Forms { - partial class FormMain + partial class FormArduino { /// /// Erforderliche Designervariable. @@ -63,8 +63,6 @@ private void InitializeComponent() this.lblWriteFileName = new System.Windows.Forms.Label(); this.txtWriteFileName = new System.Windows.Forms.TextBox(); this.groupMisc = new System.Windows.Forms.GroupBox(); - this.btnAbout = new System.Windows.Forms.Button(); - this.btnDecodeFont = new System.Windows.Forms.Button(); this.btnClearLock = new System.Windows.Forms.Button(); this.btnEraseChip = new System.Windows.Forms.Button(); this.btnClearLog = new System.Windows.Forms.Button(); @@ -73,38 +71,7 @@ private void InitializeComponent() this.radioModeExpert = new System.Windows.Forms.RadioButton(); this.radioModeSimple = new System.Windows.Forms.RadioButton(); this.groupModify = new System.Windows.Forms.GroupBox(); - this.picBackgroundColor = new System.Windows.Forms.PictureBox(); - this.picLogoForegroundColor = new System.Windows.Forms.PictureBox(); - this.picLogoBackgroundColor = new System.Windows.Forms.PictureBox(); - this.numericLogoForegroundBlue = new System.Windows.Forms.NumericUpDown(); - this.lblLogoForegroundBlue = new System.Windows.Forms.Label(); - this.numericLogoForegroundGreen = new System.Windows.Forms.NumericUpDown(); - this.lblLogoForegroundGreen = new System.Windows.Forms.Label(); - this.lblLogoForegroundRed = new System.Windows.Forms.Label(); - this.numericLogoForegroundRed = new System.Windows.Forms.NumericUpDown(); - this.chkChangeLogoForegroundColor = new System.Windows.Forms.CheckBox(); - this.txtChangeHdmi = new System.Windows.Forms.TextBox(); - this.chkChangeHdmi = new System.Windows.Forms.CheckBox(); - this.chkRemoveHdmi = new System.Windows.Forms.CheckBox(); - this.numericBackgroundBlue = new System.Windows.Forms.NumericUpDown(); - this.lblBackgroundBlue = new System.Windows.Forms.Label(); - this.numericBackgroundGreen = new System.Windows.Forms.NumericUpDown(); - this.lblBackgroundGreen = new System.Windows.Forms.Label(); - this.lblBackgroundRed = new System.Windows.Forms.Label(); - this.numericBackgroundRed = new System.Windows.Forms.NumericUpDown(); - this.numericLogoBackgroundBlue = new System.Windows.Forms.NumericUpDown(); - this.lblLogoBackgroundBlue = new System.Windows.Forms.Label(); - this.numericLogoBackgroundGreen = new System.Windows.Forms.NumericUpDown(); - this.lblLogoBackgroundGreen = new System.Windows.Forms.Label(); - this.lblLogoBackgroundRed = new System.Windows.Forms.Label(); - this.numericLogoBackgroundRed = new System.Windows.Forms.NumericUpDown(); - this.chkChangeBackgroundColor = new System.Windows.Forms.CheckBox(); - this.chkChangeLogoBackgroundColor = new System.Windows.Forms.CheckBox(); - this.chkChangeLogo = new System.Windows.Forms.CheckBox(); - this.btnModify = new System.Windows.Forms.Button(); - this.btnLogoFileNameBrowse = new System.Windows.Forms.Button(); - this.txtLogoFileName = new System.Windows.Forms.TextBox(); - this.lblLogoFileName = new System.Windows.Forms.Label(); + this.modificationSettings = new RTD266xFlash.ModificationSettings(); this.groupConnection.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numericBaudRate)).BeginInit(); this.groupRead.SuspendLayout(); @@ -115,18 +82,6 @@ private void InitializeComponent() this.groupMisc.SuspendLayout(); this.groupMode.SuspendLayout(); this.groupModify.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.picBackgroundColor)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.picLogoForegroundColor)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.picLogoBackgroundColor)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundBlue)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundGreen)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundRed)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundBlue)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundGreen)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundRed)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundBlue)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundGreen)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundRed)).BeginInit(); this.SuspendLayout(); // // groupConnection @@ -511,8 +466,6 @@ private void InitializeComponent() // // groupMisc // - this.groupMisc.Controls.Add(this.btnAbout); - this.groupMisc.Controls.Add(this.btnDecodeFont); this.groupMisc.Controls.Add(this.btnClearLock); this.groupMisc.Controls.Add(this.btnEraseChip); this.groupMisc.Controls.Add(this.btnClearLog); @@ -524,26 +477,6 @@ private void InitializeComponent() this.groupMisc.TabStop = false; this.groupMisc.Text = "Miscellaneous"; // - // btnAbout - // - this.btnAbout.Location = new System.Drawing.Point(6, 79); - this.btnAbout.Name = "btnAbout"; - this.btnAbout.Size = new System.Drawing.Size(114, 23); - this.btnAbout.TabIndex = 5; - this.btnAbout.Text = "About..."; - this.btnAbout.UseVisualStyleBackColor = true; - this.btnAbout.Click += new System.EventHandler(this.btnAbout_Click); - // - // btnDecodeFont - // - this.btnDecodeFont.Location = new System.Drawing.Point(126, 79); - this.btnDecodeFont.Name = "btnDecodeFont"; - this.btnDecodeFont.Size = new System.Drawing.Size(114, 23); - this.btnDecodeFont.TabIndex = 4; - this.btnDecodeFont.Text = "Decode font..."; - this.btnDecodeFont.UseVisualStyleBackColor = true; - this.btnDecodeFont.Click += new System.EventHandler(this.btnDecodeFont_Click); - // // btnClearLock // this.btnClearLock.Location = new System.Drawing.Point(126, 50); @@ -620,38 +553,7 @@ private void InitializeComponent() // // groupModify // - this.groupModify.Controls.Add(this.picBackgroundColor); - this.groupModify.Controls.Add(this.picLogoForegroundColor); - this.groupModify.Controls.Add(this.picLogoBackgroundColor); - this.groupModify.Controls.Add(this.numericLogoForegroundBlue); - this.groupModify.Controls.Add(this.lblLogoForegroundBlue); - this.groupModify.Controls.Add(this.numericLogoForegroundGreen); - this.groupModify.Controls.Add(this.lblLogoForegroundGreen); - this.groupModify.Controls.Add(this.lblLogoForegroundRed); - this.groupModify.Controls.Add(this.numericLogoForegroundRed); - this.groupModify.Controls.Add(this.chkChangeLogoForegroundColor); - this.groupModify.Controls.Add(this.txtChangeHdmi); - this.groupModify.Controls.Add(this.chkChangeHdmi); - this.groupModify.Controls.Add(this.chkRemoveHdmi); - this.groupModify.Controls.Add(this.numericBackgroundBlue); - this.groupModify.Controls.Add(this.lblBackgroundBlue); - this.groupModify.Controls.Add(this.numericBackgroundGreen); - this.groupModify.Controls.Add(this.lblBackgroundGreen); - this.groupModify.Controls.Add(this.lblBackgroundRed); - this.groupModify.Controls.Add(this.numericBackgroundRed); - this.groupModify.Controls.Add(this.numericLogoBackgroundBlue); - this.groupModify.Controls.Add(this.lblLogoBackgroundBlue); - this.groupModify.Controls.Add(this.numericLogoBackgroundGreen); - this.groupModify.Controls.Add(this.lblLogoBackgroundGreen); - this.groupModify.Controls.Add(this.lblLogoBackgroundRed); - this.groupModify.Controls.Add(this.numericLogoBackgroundRed); - this.groupModify.Controls.Add(this.chkChangeBackgroundColor); - this.groupModify.Controls.Add(this.chkChangeLogoBackgroundColor); - this.groupModify.Controls.Add(this.chkChangeLogo); - this.groupModify.Controls.Add(this.btnModify); - this.groupModify.Controls.Add(this.btnLogoFileNameBrowse); - this.groupModify.Controls.Add(this.txtLogoFileName); - this.groupModify.Controls.Add(this.lblLogoFileName); + this.groupModify.Controls.Add(this.modificationSettings); this.groupModify.Location = new System.Drawing.Point(314, 12); this.groupModify.Name = "groupModify"; this.groupModify.Size = new System.Drawing.Size(296, 297); @@ -659,364 +561,16 @@ private void InitializeComponent() this.groupModify.TabStop = false; this.groupModify.Text = "Modify firmware"; // - // picBackgroundColor - // - this.picBackgroundColor.BackColor = System.Drawing.Color.Black; - this.picBackgroundColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; - this.picBackgroundColor.Location = new System.Drawing.Point(247, 189); - this.picBackgroundColor.Name = "picBackgroundColor"; - this.picBackgroundColor.Size = new System.Drawing.Size(22, 22); - this.picBackgroundColor.TabIndex = 36; - this.picBackgroundColor.TabStop = false; - this.picBackgroundColor.Click += new System.EventHandler(this.picBackgroundColor_Click); - // - // picLogoForegroundColor - // - this.picLogoForegroundColor.BackColor = System.Drawing.Color.White; - this.picLogoForegroundColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; - this.picLogoForegroundColor.Location = new System.Drawing.Point(247, 138); - this.picLogoForegroundColor.Name = "picLogoForegroundColor"; - this.picLogoForegroundColor.Size = new System.Drawing.Size(22, 22); - this.picLogoForegroundColor.TabIndex = 35; - this.picLogoForegroundColor.TabStop = false; - this.picLogoForegroundColor.Click += new System.EventHandler(this.picLogoForegroundColor_Click); - // - // picLogoBackgroundColor - // - this.picLogoBackgroundColor.BackColor = System.Drawing.Color.Black; - this.picLogoBackgroundColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; - this.picLogoBackgroundColor.Location = new System.Drawing.Point(247, 87); - this.picLogoBackgroundColor.Name = "picLogoBackgroundColor"; - this.picLogoBackgroundColor.Size = new System.Drawing.Size(22, 22); - this.picLogoBackgroundColor.TabIndex = 34; - this.picLogoBackgroundColor.TabStop = false; - this.picLogoBackgroundColor.Click += new System.EventHandler(this.picLogoBackgroundColor_Click); - // - // numericLogoForegroundBlue - // - this.numericLogoForegroundBlue.Location = new System.Drawing.Point(189, 138); - this.numericLogoForegroundBlue.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoForegroundBlue.Name = "numericLogoForegroundBlue"; - this.numericLogoForegroundBlue.Size = new System.Drawing.Size(52, 22); - this.numericLogoForegroundBlue.TabIndex = 33; - this.numericLogoForegroundBlue.Value = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoForegroundBlue.ValueChanged += new System.EventHandler(this.numericLogoForeground_ValueChanged); - // - // lblLogoForegroundBlue - // - this.lblLogoForegroundBlue.AutoSize = true; - this.lblLogoForegroundBlue.Location = new System.Drawing.Point(166, 140); - this.lblLogoForegroundBlue.Name = "lblLogoForegroundBlue"; - this.lblLogoForegroundBlue.Size = new System.Drawing.Size(17, 13); - this.lblLogoForegroundBlue.TabIndex = 32; - this.lblLogoForegroundBlue.Text = "B:"; - // - // numericLogoForegroundGreen - // - this.numericLogoForegroundGreen.Location = new System.Drawing.Point(108, 138); - this.numericLogoForegroundGreen.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoForegroundGreen.Name = "numericLogoForegroundGreen"; - this.numericLogoForegroundGreen.Size = new System.Drawing.Size(52, 22); - this.numericLogoForegroundGreen.TabIndex = 31; - this.numericLogoForegroundGreen.Value = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoForegroundGreen.ValueChanged += new System.EventHandler(this.numericLogoForeground_ValueChanged); - // - // lblLogoForegroundGreen - // - this.lblLogoForegroundGreen.AutoSize = true; - this.lblLogoForegroundGreen.Location = new System.Drawing.Point(84, 140); - this.lblLogoForegroundGreen.Name = "lblLogoForegroundGreen"; - this.lblLogoForegroundGreen.Size = new System.Drawing.Size(18, 13); - this.lblLogoForegroundGreen.TabIndex = 30; - this.lblLogoForegroundGreen.Text = "G:"; - // - // lblLogoForegroundRed - // - this.lblLogoForegroundRed.AutoSize = true; - this.lblLogoForegroundRed.Location = new System.Drawing.Point(3, 140); - this.lblLogoForegroundRed.Name = "lblLogoForegroundRed"; - this.lblLogoForegroundRed.Size = new System.Drawing.Size(17, 13); - this.lblLogoForegroundRed.TabIndex = 29; - this.lblLogoForegroundRed.Text = "R:"; - // - // numericLogoForegroundRed - // - this.numericLogoForegroundRed.Location = new System.Drawing.Point(26, 138); - this.numericLogoForegroundRed.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoForegroundRed.Name = "numericLogoForegroundRed"; - this.numericLogoForegroundRed.Size = new System.Drawing.Size(52, 22); - this.numericLogoForegroundRed.TabIndex = 28; - this.numericLogoForegroundRed.Value = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoForegroundRed.ValueChanged += new System.EventHandler(this.numericLogoForeground_ValueChanged); - // - // chkChangeLogoForegroundColor - // - this.chkChangeLogoForegroundColor.AutoSize = true; - this.chkChangeLogoForegroundColor.Location = new System.Drawing.Point(6, 115); - this.chkChangeLogoForegroundColor.Name = "chkChangeLogoForegroundColor"; - this.chkChangeLogoForegroundColor.Size = new System.Drawing.Size(185, 17); - this.chkChangeLogoForegroundColor.TabIndex = 27; - this.chkChangeLogoForegroundColor.Text = "Change logo foreground color"; - this.chkChangeLogoForegroundColor.UseVisualStyleBackColor = true; - this.chkChangeLogoForegroundColor.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); - // - // txtChangeHdmi - // - this.txtChangeHdmi.Location = new System.Drawing.Point(177, 238); - this.txtChangeHdmi.Name = "txtChangeHdmi"; - this.txtChangeHdmi.Size = new System.Drawing.Size(113, 22); - this.txtChangeHdmi.TabIndex = 26; - this.txtChangeHdmi.Text = "HDMI"; - // - // chkChangeHdmi - // - this.chkChangeHdmi.AutoSize = true; - this.chkChangeHdmi.Location = new System.Drawing.Point(6, 240); - this.chkChangeHdmi.Name = "chkChangeHdmi"; - this.chkChangeHdmi.Size = new System.Drawing.Size(165, 17); - this.chkChangeHdmi.TabIndex = 25; - this.chkChangeHdmi.Text = "Change \"HDMI\" pop-up to:"; - this.chkChangeHdmi.UseVisualStyleBackColor = true; - this.chkChangeHdmi.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); - // - // chkRemoveHdmi - // - this.chkRemoveHdmi.AutoSize = true; - this.chkRemoveHdmi.Location = new System.Drawing.Point(6, 217); - this.chkRemoveHdmi.Name = "chkRemoveHdmi"; - this.chkRemoveHdmi.Size = new System.Drawing.Size(148, 17); - this.chkRemoveHdmi.TabIndex = 24; - this.chkRemoveHdmi.Text = "Remove \"HDMI\" pop-up"; - this.chkRemoveHdmi.UseVisualStyleBackColor = true; - this.chkRemoveHdmi.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); - // - // numericBackgroundBlue - // - this.numericBackgroundBlue.Location = new System.Drawing.Point(189, 189); - this.numericBackgroundBlue.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericBackgroundBlue.Name = "numericBackgroundBlue"; - this.numericBackgroundBlue.Size = new System.Drawing.Size(52, 22); - this.numericBackgroundBlue.TabIndex = 23; - this.numericBackgroundBlue.ValueChanged += new System.EventHandler(this.numericBackground_ValueChanged); - // - // lblBackgroundBlue - // - this.lblBackgroundBlue.AutoSize = true; - this.lblBackgroundBlue.Location = new System.Drawing.Point(166, 191); - this.lblBackgroundBlue.Name = "lblBackgroundBlue"; - this.lblBackgroundBlue.Size = new System.Drawing.Size(17, 13); - this.lblBackgroundBlue.TabIndex = 22; - this.lblBackgroundBlue.Text = "B:"; + // modificationSettings // - // numericBackgroundGreen - // - this.numericBackgroundGreen.Location = new System.Drawing.Point(108, 189); - this.numericBackgroundGreen.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericBackgroundGreen.Name = "numericBackgroundGreen"; - this.numericBackgroundGreen.Size = new System.Drawing.Size(52, 22); - this.numericBackgroundGreen.TabIndex = 21; - this.numericBackgroundGreen.ValueChanged += new System.EventHandler(this.numericBackground_ValueChanged); - // - // lblBackgroundGreen - // - this.lblBackgroundGreen.AutoSize = true; - this.lblBackgroundGreen.Location = new System.Drawing.Point(84, 191); - this.lblBackgroundGreen.Name = "lblBackgroundGreen"; - this.lblBackgroundGreen.Size = new System.Drawing.Size(18, 13); - this.lblBackgroundGreen.TabIndex = 20; - this.lblBackgroundGreen.Text = "G:"; - // - // lblBackgroundRed - // - this.lblBackgroundRed.AutoSize = true; - this.lblBackgroundRed.Location = new System.Drawing.Point(3, 191); - this.lblBackgroundRed.Name = "lblBackgroundRed"; - this.lblBackgroundRed.Size = new System.Drawing.Size(17, 13); - this.lblBackgroundRed.TabIndex = 19; - this.lblBackgroundRed.Text = "R:"; - // - // numericBackgroundRed - // - this.numericBackgroundRed.Location = new System.Drawing.Point(26, 189); - this.numericBackgroundRed.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericBackgroundRed.Name = "numericBackgroundRed"; - this.numericBackgroundRed.Size = new System.Drawing.Size(52, 22); - this.numericBackgroundRed.TabIndex = 18; - this.numericBackgroundRed.ValueChanged += new System.EventHandler(this.numericBackground_ValueChanged); + this.modificationSettings.Location = new System.Drawing.Point(3, 16); + this.modificationSettings.ModifyEnabled = true; + this.modificationSettings.Name = "modificationSettings"; + this.modificationSettings.Size = new System.Drawing.Size(291, 275); + this.modificationSettings.TabIndex = 0; + this.modificationSettings.ModifyClickedEvent += new RTD266xFlash.ModificationSettings.ModifyClickedHandler(this.btnModify_Click); // - // numericLogoBackgroundBlue - // - this.numericLogoBackgroundBlue.Location = new System.Drawing.Point(189, 87); - this.numericLogoBackgroundBlue.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoBackgroundBlue.Name = "numericLogoBackgroundBlue"; - this.numericLogoBackgroundBlue.Size = new System.Drawing.Size(52, 22); - this.numericLogoBackgroundBlue.TabIndex = 17; - this.numericLogoBackgroundBlue.ValueChanged += new System.EventHandler(this.numericLogoBackground_ValueChanged); - // - // lblLogoBackgroundBlue - // - this.lblLogoBackgroundBlue.AutoSize = true; - this.lblLogoBackgroundBlue.Location = new System.Drawing.Point(166, 89); - this.lblLogoBackgroundBlue.Name = "lblLogoBackgroundBlue"; - this.lblLogoBackgroundBlue.Size = new System.Drawing.Size(17, 13); - this.lblLogoBackgroundBlue.TabIndex = 16; - this.lblLogoBackgroundBlue.Text = "B:"; - // - // numericLogoBackgroundGreen - // - this.numericLogoBackgroundGreen.Location = new System.Drawing.Point(108, 87); - this.numericLogoBackgroundGreen.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoBackgroundGreen.Name = "numericLogoBackgroundGreen"; - this.numericLogoBackgroundGreen.Size = new System.Drawing.Size(52, 22); - this.numericLogoBackgroundGreen.TabIndex = 15; - this.numericLogoBackgroundGreen.ValueChanged += new System.EventHandler(this.numericLogoBackground_ValueChanged); - // - // lblLogoBackgroundGreen - // - this.lblLogoBackgroundGreen.AutoSize = true; - this.lblLogoBackgroundGreen.Location = new System.Drawing.Point(84, 89); - this.lblLogoBackgroundGreen.Name = "lblLogoBackgroundGreen"; - this.lblLogoBackgroundGreen.Size = new System.Drawing.Size(18, 13); - this.lblLogoBackgroundGreen.TabIndex = 14; - this.lblLogoBackgroundGreen.Text = "G:"; - // - // lblLogoBackgroundRed - // - this.lblLogoBackgroundRed.AutoSize = true; - this.lblLogoBackgroundRed.Location = new System.Drawing.Point(3, 89); - this.lblLogoBackgroundRed.Name = "lblLogoBackgroundRed"; - this.lblLogoBackgroundRed.Size = new System.Drawing.Size(17, 13); - this.lblLogoBackgroundRed.TabIndex = 13; - this.lblLogoBackgroundRed.Text = "R:"; - // - // numericLogoBackgroundRed - // - this.numericLogoBackgroundRed.Location = new System.Drawing.Point(26, 87); - this.numericLogoBackgroundRed.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.numericLogoBackgroundRed.Name = "numericLogoBackgroundRed"; - this.numericLogoBackgroundRed.Size = new System.Drawing.Size(52, 22); - this.numericLogoBackgroundRed.TabIndex = 12; - this.numericLogoBackgroundRed.ValueChanged += new System.EventHandler(this.numericLogoBackground_ValueChanged); - // - // chkChangeBackgroundColor - // - this.chkChangeBackgroundColor.AutoSize = true; - this.chkChangeBackgroundColor.Location = new System.Drawing.Point(6, 166); - this.chkChangeBackgroundColor.Name = "chkChangeBackgroundColor"; - this.chkChangeBackgroundColor.Size = new System.Drawing.Size(200, 17); - this.chkChangeBackgroundColor.TabIndex = 11; - this.chkChangeBackgroundColor.Text = "Change display background color"; - this.chkChangeBackgroundColor.UseVisualStyleBackColor = true; - this.chkChangeBackgroundColor.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); - // - // chkChangeLogoBackgroundColor - // - this.chkChangeLogoBackgroundColor.AutoSize = true; - this.chkChangeLogoBackgroundColor.Location = new System.Drawing.Point(6, 64); - this.chkChangeLogoBackgroundColor.Name = "chkChangeLogoBackgroundColor"; - this.chkChangeLogoBackgroundColor.Size = new System.Drawing.Size(188, 17); - this.chkChangeLogoBackgroundColor.TabIndex = 10; - this.chkChangeLogoBackgroundColor.Text = "Change logo background color"; - this.chkChangeLogoBackgroundColor.UseVisualStyleBackColor = true; - this.chkChangeLogoBackgroundColor.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); - // - // chkChangeLogo - // - this.chkChangeLogo.AutoSize = true; - this.chkChangeLogo.Location = new System.Drawing.Point(6, 19); - this.chkChangeLogo.Name = "chkChangeLogo"; - this.chkChangeLogo.Size = new System.Drawing.Size(93, 17); - this.chkChangeLogo.TabIndex = 9; - this.chkChangeLogo.Text = "Change logo"; - this.chkChangeLogo.UseVisualStyleBackColor = true; - this.chkChangeLogo.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); - // - // btnModify - // - this.btnModify.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.btnModify.Location = new System.Drawing.Point(6, 268); - this.btnModify.Name = "btnModify"; - this.btnModify.Size = new System.Drawing.Size(114, 23); - this.btnModify.TabIndex = 8; - this.btnModify.Text = "Modify firmware"; - this.btnModify.UseVisualStyleBackColor = true; - this.btnModify.Click += new System.EventHandler(this.btnModify_Click); - // - // btnLogoFileNameBrowse - // - this.btnLogoFileNameBrowse.Location = new System.Drawing.Point(259, 36); - this.btnLogoFileNameBrowse.Name = "btnLogoFileNameBrowse"; - this.btnLogoFileNameBrowse.Size = new System.Drawing.Size(31, 22); - this.btnLogoFileNameBrowse.TabIndex = 4; - this.btnLogoFileNameBrowse.Text = "..."; - this.btnLogoFileNameBrowse.UseVisualStyleBackColor = true; - this.btnLogoFileNameBrowse.Click += new System.EventHandler(this.btnLogoFileNameBrowse_Click); - // - // txtLogoFileName - // - this.txtLogoFileName.Location = new System.Drawing.Point(95, 36); - this.txtLogoFileName.Name = "txtLogoFileName"; - this.txtLogoFileName.Size = new System.Drawing.Size(158, 22); - this.txtLogoFileName.TabIndex = 3; - // - // lblLogoFileName - // - this.lblLogoFileName.AutoSize = true; - this.lblLogoFileName.Location = new System.Drawing.Point(3, 39); - this.lblLogoFileName.Name = "lblLogoFileName"; - this.lblLogoFileName.Size = new System.Drawing.Size(86, 13); - this.lblLogoFileName.TabIndex = 2; - this.lblLogoFileName.Text = "Logo input file:"; - // - // FormMain + // FormArduino // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -1030,7 +584,7 @@ private void InitializeComponent() this.Controls.Add(this.groupConnection); this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.MinimumSize = new System.Drawing.Size(800, 500); - this.Name = "FormMain"; + this.Name = "FormArduino"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "RTD266x EEPROM Flasher"; this.Load += new System.EventHandler(this.FormMain_Load); @@ -1048,19 +602,6 @@ private void InitializeComponent() this.groupMode.ResumeLayout(false); this.groupMode.PerformLayout(); this.groupModify.ResumeLayout(false); - this.groupModify.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.picBackgroundColor)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.picLogoForegroundColor)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.picLogoBackgroundColor)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundBlue)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundGreen)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundRed)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundBlue)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundGreen)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundRed)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundBlue)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundGreen)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundRed)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -1096,7 +637,6 @@ private void InitializeComponent() private System.Windows.Forms.Button btnClearLog; private System.Windows.Forms.Button btnEraseChip; private System.Windows.Forms.Button btnClearLock; - private System.Windows.Forms.Button btnDecodeFont; private System.Windows.Forms.TextBox txtReadStartAddress; private System.Windows.Forms.TextBox txtReadLength; private System.Windows.Forms.TextBox txtWriteStartAddress; @@ -1110,41 +650,9 @@ private void InitializeComponent() private System.Windows.Forms.Label lblWriteStartAddressHex; private System.Windows.Forms.Label lblWriteStartAddressDec; private System.Windows.Forms.GroupBox groupModify; - private System.Windows.Forms.Label lblLogoFileName; - private System.Windows.Forms.Button btnModify; - private System.Windows.Forms.Button btnLogoFileNameBrowse; - private System.Windows.Forms.TextBox txtLogoFileName; private System.Windows.Forms.CheckBox chkReadConsole; private System.Windows.Forms.CheckBox chkReadFile; - private System.Windows.Forms.Button btnAbout; - private System.Windows.Forms.CheckBox chkChangeLogo; - private System.Windows.Forms.CheckBox chkChangeBackgroundColor; - private System.Windows.Forms.CheckBox chkChangeLogoBackgroundColor; - private System.Windows.Forms.NumericUpDown numericLogoBackgroundBlue; - private System.Windows.Forms.Label lblLogoBackgroundBlue; - private System.Windows.Forms.NumericUpDown numericLogoBackgroundGreen; - private System.Windows.Forms.Label lblLogoBackgroundGreen; - private System.Windows.Forms.Label lblLogoBackgroundRed; - private System.Windows.Forms.NumericUpDown numericLogoBackgroundRed; - private System.Windows.Forms.NumericUpDown numericBackgroundBlue; - private System.Windows.Forms.Label lblBackgroundBlue; - private System.Windows.Forms.NumericUpDown numericBackgroundGreen; - private System.Windows.Forms.Label lblBackgroundGreen; - private System.Windows.Forms.Label lblBackgroundRed; - private System.Windows.Forms.NumericUpDown numericBackgroundRed; - private System.Windows.Forms.TextBox txtChangeHdmi; - private System.Windows.Forms.CheckBox chkChangeHdmi; - private System.Windows.Forms.CheckBox chkRemoveHdmi; - private System.Windows.Forms.NumericUpDown numericLogoForegroundBlue; - private System.Windows.Forms.Label lblLogoForegroundBlue; - private System.Windows.Forms.NumericUpDown numericLogoForegroundGreen; - private System.Windows.Forms.Label lblLogoForegroundGreen; - private System.Windows.Forms.Label lblLogoForegroundRed; - private System.Windows.Forms.NumericUpDown numericLogoForegroundRed; - private System.Windows.Forms.CheckBox chkChangeLogoForegroundColor; - private System.Windows.Forms.PictureBox picLogoBackgroundColor; - private System.Windows.Forms.PictureBox picBackgroundColor; - private System.Windows.Forms.PictureBox picLogoForegroundColor; + private ModificationSettings modificationSettings; } } diff --git a/RTD266xFlash/RTD266xFlash/FormMain.cs b/RTD266xFlash/RTD266xFlash/Forms/FormArduino.cs similarity index 64% rename from RTD266xFlash/RTD266xFlash/FormMain.cs rename to RTD266xFlash/RTD266xFlash/Forms/FormArduino.cs index e91edab..cb91cbd 100644 --- a/RTD266xFlash/RTD266xFlash/FormMain.cs +++ b/RTD266xFlash/RTD266xFlash/Forms/FormArduino.cs @@ -1,14 +1,13 @@ -using RTD266xFlash.BackgroundWorkers; -using System; -using System.Drawing; +using System; using System.IO; using System.IO.Ports; using System.Text; using System.Windows.Forms; +using RTD266xFlash.BackgroundWorkers; -namespace RTD266xFlash +namespace RTD266xFlash.Forms { - public partial class FormMain : Form + public partial class FormArduino : Form { private SerialPort _comPort; @@ -16,7 +15,7 @@ public partial class FormMain : Form private bool _guiUpdate; - public FormMain() + public FormArduino() { InitializeComponent(); } @@ -42,54 +41,7 @@ private void UpdateConnected(bool connected) btnReadStatus.Enabled = connected; btnEraseChip.Enabled = connected; btnClearLock.Enabled = connected; - btnModify.Enabled = connected; - } - - private void UpdateModifyFirmware() - { - _guiUpdate = true; - - txtLogoFileName.Enabled = chkChangeLogo.Checked; - btnLogoFileNameBrowse.Enabled = chkChangeLogo.Checked; - - numericLogoBackgroundRed.Enabled = chkChangeLogoBackgroundColor.Checked; - numericLogoBackgroundGreen.Enabled = chkChangeLogoBackgroundColor.Checked; - numericLogoBackgroundBlue.Enabled = chkChangeLogoBackgroundColor.Checked; - picLogoBackgroundColor.Enabled = chkChangeLogoBackgroundColor.Checked; - - numericLogoForegroundRed.Enabled = chkChangeLogoForegroundColor.Checked; - numericLogoForegroundGreen.Enabled = chkChangeLogoForegroundColor.Checked; - numericLogoForegroundBlue.Enabled = chkChangeLogoForegroundColor.Checked; - picLogoForegroundColor.Enabled = chkChangeLogoForegroundColor.Checked; - - numericBackgroundRed.Enabled = chkChangeBackgroundColor.Checked; - numericBackgroundGreen.Enabled = chkChangeBackgroundColor.Checked; - numericBackgroundBlue.Enabled = chkChangeBackgroundColor.Checked; - picBackgroundColor.Enabled = chkChangeBackgroundColor.Checked; - - if (chkRemoveHdmi.Checked) - { - chkChangeHdmi.Checked = false; - chkChangeHdmi.Enabled = false; - } - else - { - chkChangeHdmi.Enabled = true; - } - - if (chkChangeHdmi.Checked) - { - chkRemoveHdmi.Checked = false; - chkRemoveHdmi.Enabled = false; - } - else - { - chkRemoveHdmi.Enabled = true; - } - - txtChangeHdmi.Enabled = chkChangeHdmi.Checked; - - _guiUpdate = false; + modificationSettings.ModifyEnabled = connected; } private void UpdateBackgroundWorkerActive(bool active) @@ -149,95 +101,6 @@ private void UpdateMode() groupWrite.Visible = expertMode; btnEraseChip.Visible = expertMode; btnClearLock.Visible = expertMode; - btnDecodeFont.Visible = expertMode; - } - - private void ShowErrorMessageBox(string errorMessage) - { - MessageBox.Show(errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); - } - - private void FillColorBox(PictureBox pictureBox, int red, int green, int blue) - { - pictureBox.BackColor = Color.FromArgb(red, green, blue); - } - - private void ShowColorDialog(NumericUpDown numericRed, NumericUpDown numericGreen, NumericUpDown numericBlue) - { - ColorDialog colorDialog = new ColorDialog(); - - colorDialog.Color = Color.FromArgb((int)numericRed.Value, (int)numericGreen.Value, (int)numericBlue.Value); - - if (colorDialog.ShowDialog() == DialogResult.OK) - { - numericRed.Value = colorDialog.Color.R; - numericGreen.Value = colorDialog.Color.G; - numericBlue.Value = colorDialog.Color.B; - } - } - - /// - /// Helper function to calculate the hash of a new firmware - /// - private void CalculateFirmwareHash() - { - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Filter = "All files (*.*)|*.*"; - - if (openFileDialog.ShowDialog() != DialogResult.OK) - { - return; - } - - byte[] firmwareData = File.ReadAllBytes(openFileDialog.FileName); - - HashInfo hashInfo = new HashInfo(0, 0x80000, string.Empty, new[] - { - new HashSkip(0xD2F6 + 0x1D, 48), - new HashSkip(0x12346, 16), - new HashSkip(0x13A31, 48), - new HashSkip(0x14733, 1), - new HashSkip(0x260D8, 1507) - }); - - MessageBox.Show(hashInfo.GetHash(firmwareData), "Firmware hash", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - - /// - /// Helper function to identify a firmware - /// - private void IdentifyFirmware() - { - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Filter = "All files (*.*)|*.*"; - - byte[] firmware; - - if (openFileDialog.ShowDialog() != DialogResult.OK) - { - return; - } - - firmware = File.ReadAllBytes(openFileDialog.FileName); - - Firmware detectedFirmware = null; - - foreach (Firmware fw in Firmware.KnownFirmwares) - { - if (fw.CheckHash(firmware)) - { - detectedFirmware = fw; - break; - } - } - - if (detectedFirmware == null) - { - MessageBox.Show("No matching firmware found!", "Firmware identification", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); - return; - } - - MessageBox.Show($"Detected firmware is {detectedFirmware.Name}.", "Firmware identification", MessageBoxButtons.OK, MessageBoxIcon.Information); } #region Background workers @@ -323,7 +186,7 @@ private void FormMain_Load(object sender, EventArgs e) UpdateConnected(false); UpdateMode(); - UpdateModifyFirmware(); + modificationSettings.UpdateModifyFirmware(); AppendConsoleText("Configure the connection and click \"Connect\"\r\n"); } @@ -560,19 +423,6 @@ private void btnClearLock_Click(object sender, EventArgs e) } } - private void btnAbout_Click(object sender, EventArgs e) - { - FormAbout formAbout = new FormAbout(); - - formAbout.ShowDialog(); - } - - private void btnDecodeFont_Click(object sender, EventArgs e) - { - FormFont formFont = new FormFont(); - formFont.Show(); - } - private void txtReadStartAddress_TextChanged(object sender, EventArgs e) { if (_guiUpdate) @@ -654,84 +504,24 @@ private void radioModeExpert_CheckedChanged(object sender, EventArgs e) Properties.Settings.Default.Save(); } - private void btnLogoFileNameBrowse_Click(object sender, EventArgs e) - { - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Filter = "Images (*.png, *.bmp, *.tif, *.tiff)|*.png;*.bmp;*.tif;*.tiff"; - - if (openFileDialog.ShowDialog() == DialogResult.OK) - { - string error; - - if (!FontCoder.CheckFile(openFileDialog.FileName, FontCoder.FontWidthKedei, FontCoder.FontHeightKedei, out error)) - { - ShowErrorMessageBox(error); - } - else - { - txtLogoFileName.Text = openFileDialog.FileName; - } - } - } - - private void btnModify_Click(object sender, EventArgs e) + private void btnModify_Click() { UpdateBackgroundWorkerActive(true); ModifyFirmwareWorker changeLogoWorker = new ModifyFirmwareWorker( _rtd, - chkChangeLogo.Checked ? txtLogoFileName.Text : null, - chkChangeLogoBackgroundColor.Checked ? Color.FromArgb((int)numericLogoBackgroundRed.Value, (int)numericLogoBackgroundGreen.Value, (int)numericLogoBackgroundBlue.Value) : Color.Empty, - chkChangeLogoForegroundColor.Checked ? Color.FromArgb((int)numericLogoForegroundRed.Value, (int)numericLogoForegroundGreen.Value, (int)numericLogoForegroundBlue.Value) : Color.Empty, - chkChangeBackgroundColor.Checked ? Color.FromArgb((int)numericBackgroundRed.Value, (int)numericBackgroundGreen.Value, (int)numericBackgroundBlue.Value) : Color.Empty, - chkRemoveHdmi.Checked, - chkChangeHdmi.Checked ? txtChangeHdmi.Text : null); + modificationSettings.LogoFileName, + modificationSettings.LogoBackgroundColor, + modificationSettings.LogoForegroundColor, + modificationSettings.BackgroundColor, + modificationSettings.RemoveHdmiPopup, + modificationSettings.HdmiReplacementText); changeLogoWorker.WorkerReportStatus += AppendConsoleText; changeLogoWorker.ModifyFirmwareWorkerFinished += ModifyFirmwareWorkerFinished; changeLogoWorker.Start(); } - private void chkModifyFirmware_CheckedChanged(object sender, EventArgs e) - { - if (_guiUpdate) - { - return; - } - - UpdateModifyFirmware(); - } - - private void numericLogoBackground_ValueChanged(object sender, EventArgs e) - { - FillColorBox(picLogoBackgroundColor, (int)numericLogoBackgroundRed.Value, (int)numericLogoBackgroundGreen.Value, (int)numericLogoBackgroundBlue.Value); - } - - private void numericLogoForeground_ValueChanged(object sender, EventArgs e) - { - FillColorBox(picLogoForegroundColor, (int)numericLogoForegroundRed.Value, (int)numericLogoForegroundGreen.Value, (int)numericLogoForegroundBlue.Value); - } - - private void numericBackground_ValueChanged(object sender, EventArgs e) - { - FillColorBox(picBackgroundColor, (int)numericBackgroundRed.Value, (int)numericBackgroundGreen.Value, (int)numericBackgroundBlue.Value); - } - - private void picLogoBackgroundColor_Click(object sender, EventArgs e) - { - ShowColorDialog(numericLogoBackgroundRed, numericLogoBackgroundGreen, numericLogoBackgroundBlue); - } - - private void picLogoForegroundColor_Click(object sender, EventArgs e) - { - ShowColorDialog(numericLogoForegroundRed, numericLogoForegroundGreen, numericLogoForegroundBlue); - } - - private void picBackgroundColor_Click(object sender, EventArgs e) - { - ShowColorDialog(numericBackgroundRed, numericBackgroundGreen, numericBackgroundBlue); - } - #endregion } } diff --git a/RTD266xFlash/RTD266xFlash/FormFont.resx b/RTD266xFlash/RTD266xFlash/Forms/FormArduino.resx similarity index 100% rename from RTD266xFlash/RTD266xFlash/FormFont.resx rename to RTD266xFlash/RTD266xFlash/Forms/FormArduino.resx diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormExtras.Designer.cs b/RTD266xFlash/RTD266xFlash/Forms/FormExtras.Designer.cs new file mode 100644 index 0000000..20049ab --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormExtras.Designer.cs @@ -0,0 +1,96 @@ +namespace RTD266xFlash.Forms +{ + partial class FormExtras + { + /// + /// 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() + { + this.btnIdentifyFirmware = new System.Windows.Forms.Button(); + this.btnCalculateHash = new System.Windows.Forms.Button(); + this.btnDecodeFont = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // btnIdentifyFirmware + // + this.btnIdentifyFirmware.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.btnIdentifyFirmware.Location = new System.Drawing.Point(12, 12); + this.btnIdentifyFirmware.Name = "btnIdentifyFirmware"; + this.btnIdentifyFirmware.Size = new System.Drawing.Size(144, 23); + this.btnIdentifyFirmware.TabIndex = 6; + this.btnIdentifyFirmware.Text = "Identify firmware..."; + this.btnIdentifyFirmware.UseVisualStyleBackColor = true; + this.btnIdentifyFirmware.Click += new System.EventHandler(this.btnIdentifyFirmware_Click); + // + // btnCalculateHash + // + this.btnCalculateHash.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.btnCalculateHash.Location = new System.Drawing.Point(12, 41); + this.btnCalculateHash.Name = "btnCalculateHash"; + this.btnCalculateHash.Size = new System.Drawing.Size(144, 23); + this.btnCalculateHash.TabIndex = 7; + this.btnCalculateHash.Text = "Calculate firmware hash..."; + this.btnCalculateHash.UseVisualStyleBackColor = true; + this.btnCalculateHash.Click += new System.EventHandler(this.btnCalculateHash_Click); + // + // btnDecodeFont + // + this.btnDecodeFont.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.btnDecodeFont.Location = new System.Drawing.Point(12, 70); + this.btnDecodeFont.Name = "btnDecodeFont"; + this.btnDecodeFont.Size = new System.Drawing.Size(144, 23); + this.btnDecodeFont.TabIndex = 8; + this.btnDecodeFont.Text = "Decode font..."; + this.btnDecodeFont.UseVisualStyleBackColor = true; + this.btnDecodeFont.Click += new System.EventHandler(this.btnDecodeFont_Click); + // + // FormExtras + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(168, 104); + this.Controls.Add(this.btnDecodeFont); + this.Controls.Add(this.btnCalculateHash); + this.Controls.Add(this.btnIdentifyFirmware); + this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.Name = "FormExtras"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "RTD266x extras"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button btnIdentifyFirmware; + private System.Windows.Forms.Button btnCalculateHash; + private System.Windows.Forms.Button btnDecodeFont; + } +} \ No newline at end of file diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormExtras.cs b/RTD266xFlash/RTD266xFlash/Forms/FormExtras.cs new file mode 100644 index 0000000..d222422 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormExtras.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Windows.Forms; + +namespace RTD266xFlash.Forms +{ + public partial class FormExtras : Form + { + public FormExtras() + { + InitializeComponent(); + } + + private void btnIdentifyFirmware_Click(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "All files (*.*)|*.*"; + + byte[] firmware; + + if (openFileDialog.ShowDialog() != DialogResult.OK) + { + return; + } + + try + { + firmware = File.ReadAllBytes(openFileDialog.FileName); + } + catch (Exception ex) + { + MessageBox.Show($"Could not open file {openFileDialog.FileName}! {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + + Firmware detectedFirmware = null; + + foreach (Firmware fw in Firmware.KnownFirmwares) + { + if (fw.CheckHash(firmware)) + { + detectedFirmware = fw; + break; + } + } + + if (detectedFirmware == null) + { + MessageBox.Show("No matching firmware found!", "Firmware identification", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + + MessageBox.Show($"Detected firmware is {detectedFirmware.Name}.", "Firmware identification", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void btnCalculateHash_Click(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "All files (*.*)|*.*"; + + if (openFileDialog.ShowDialog() != DialogResult.OK) + { + return; + } + + byte[] firmwareData = File.ReadAllBytes(openFileDialog.FileName); + + HashInfo hashInfo = new HashInfo(0, 0x80000, string.Empty, new[] + { + new HashSkip(0xD2F6 + 0x1D, 48), + new HashSkip(0x12346, 16), + new HashSkip(0x13A31, 48), + new HashSkip(0x14733, 1), + new HashSkip(0x260D8, 1507) + }); + + MessageBox.Show(hashInfo.GetHash(firmwareData), "Firmware hash", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void btnDecodeFont_Click(object sender, EventArgs e) + { + FormFont formFont = new FormFont(); + formFont.Show(); + } + } +} diff --git a/RTD266xFlash/RTD266xFlash/FormMain.resx b/RTD266xFlash/RTD266xFlash/Forms/FormExtras.resx similarity index 100% rename from RTD266xFlash/RTD266xFlash/FormMain.resx rename to RTD266xFlash/RTD266xFlash/Forms/FormExtras.resx diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.Designer.cs b/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.Designer.cs new file mode 100644 index 0000000..b62d2ee --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.Designer.cs @@ -0,0 +1,129 @@ +namespace RTD266xFlash.Forms +{ + partial class FormFileImage + { + /// + /// 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() + { + this.groupModify = new System.Windows.Forms.GroupBox(); + this.modificationSettings = new RTD266xFlash.ModificationSettings(); + this.groupInputFile = new System.Windows.Forms.GroupBox(); + this.btnInputFileNameBrowse = new System.Windows.Forms.Button(); + this.lblInputFileName = new System.Windows.Forms.Label(); + this.txtInputFileName = new System.Windows.Forms.TextBox(); + this.groupModify.SuspendLayout(); + this.groupInputFile.SuspendLayout(); + this.SuspendLayout(); + // + // groupModify + // + this.groupModify.Controls.Add(this.modificationSettings); + this.groupModify.Location = new System.Drawing.Point(13, 71); + this.groupModify.Name = "groupModify"; + this.groupModify.Size = new System.Drawing.Size(296, 297); + this.groupModify.TabIndex = 8; + this.groupModify.TabStop = false; + this.groupModify.Text = "Modification options"; + // + // modificationSettings + // + this.modificationSettings.Location = new System.Drawing.Point(3, 16); + this.modificationSettings.ModifyEnabled = true; + this.modificationSettings.Name = "modificationSettings"; + this.modificationSettings.Size = new System.Drawing.Size(291, 275); + this.modificationSettings.TabIndex = 0; + this.modificationSettings.ModifyClickedEvent += new RTD266xFlash.ModificationSettings.ModifyClickedHandler(this.modificationSettings_ModifyClickedEvent); + // + // groupInputFile + // + this.groupInputFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupInputFile.Controls.Add(this.btnInputFileNameBrowse); + this.groupInputFile.Controls.Add(this.lblInputFileName); + this.groupInputFile.Controls.Add(this.txtInputFileName); + this.groupInputFile.Location = new System.Drawing.Point(12, 12); + this.groupInputFile.Name = "groupInputFile"; + this.groupInputFile.Size = new System.Drawing.Size(297, 53); + this.groupInputFile.TabIndex = 9; + this.groupInputFile.TabStop = false; + this.groupInputFile.Text = "Firmware image file"; + // + // btnInputFileNameBrowse + // + this.btnInputFileNameBrowse.Location = new System.Drawing.Point(261, 20); + this.btnInputFileNameBrowse.Name = "btnInputFileNameBrowse"; + this.btnInputFileNameBrowse.Size = new System.Drawing.Size(31, 22); + this.btnInputFileNameBrowse.TabIndex = 11; + this.btnInputFileNameBrowse.Text = "..."; + this.btnInputFileNameBrowse.UseVisualStyleBackColor = true; + this.btnInputFileNameBrowse.Click += new System.EventHandler(this.btnInputFileNameBrowse_Click_1); + // + // lblInputFileName + // + this.lblInputFileName.AutoSize = true; + this.lblInputFileName.Location = new System.Drawing.Point(5, 23); + this.lblInputFileName.Name = "lblInputFileName"; + this.lblInputFileName.Size = new System.Drawing.Size(57, 13); + this.lblInputFileName.TabIndex = 10; + this.lblInputFileName.Text = "Input file:"; + // + // txtInputFileName + // + this.txtInputFileName.Location = new System.Drawing.Point(68, 20); + this.txtInputFileName.Name = "txtInputFileName"; + this.txtInputFileName.Size = new System.Drawing.Size(187, 22); + this.txtInputFileName.TabIndex = 9; + // + // FormFileImage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(321, 378); + this.Controls.Add(this.groupInputFile); + this.Controls.Add(this.groupModify); + this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "FormFileImage"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "RTD266xFlash"; + this.groupModify.ResumeLayout(false); + this.groupInputFile.ResumeLayout(false); + this.groupInputFile.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupModify; + private ModificationSettings modificationSettings; + private System.Windows.Forms.GroupBox groupInputFile; + private System.Windows.Forms.Button btnInputFileNameBrowse; + private System.Windows.Forms.Label lblInputFileName; + private System.Windows.Forms.TextBox txtInputFileName; + } +} \ No newline at end of file diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.cs b/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.cs new file mode 100644 index 0000000..702df48 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.cs @@ -0,0 +1,141 @@ +using System; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +namespace RTD266xFlash.Forms +{ + public partial class FormFileImage : Form + { + public FormFileImage() + { + InitializeComponent(); + + modificationSettings.UpdateModifyFirmware(); + } + + private void ShowErrorMessage(string errorMessage) + { + MessageBox.Show(errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } + + private void btnInputFileNameBrowse_Click_1(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "All files (*.*)|*.*"; + + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + txtInputFileName.Text = openFileDialog.FileName; + } + } + + private void modificationSettings_ModifyClickedEvent() + { + string error; + string inputFileName = txtInputFileName.Text; + + if (string.IsNullOrEmpty(inputFileName)) + { + ShowErrorMessage("No input file specified!"); + return; + } + + if (!File.Exists(inputFileName)) + { + ShowErrorMessage($"The file {inputFileName} does not exist!"); + return; + } + + if (!string.IsNullOrEmpty(modificationSettings.LogoFileName)) + { + if (!FontCoder.CheckFile(modificationSettings.LogoFileName, FontCoder.FontWidthKedei, FontCoder.FontHeightKedei, out error)) + { + ShowErrorMessage(error); + return; + } + } + + if (!string.IsNullOrEmpty(modificationSettings.HdmiReplacementText)) + { + if (!FirmwareModifier.CheckHdmiReplacement(modificationSettings.HdmiReplacementText, out error)) + { + ShowErrorMessage($"Invalid HDMI replacement text: {error}"); + return; + } + } + + byte[] firmware; + + try + { + firmware = File.ReadAllBytes(inputFileName); + } + catch (Exception ex) + { + ShowErrorMessage($"Cannot load firmware from file {inputFileName}! {ex.Message}"); + return; + } + + Firmware detectedFirmware = FirmwareModifier.DetectFirmware(firmware); + + if (detectedFirmware == null) + { + ShowErrorMessage("Error! Unknown firmware. You can send your firmware to the author (floppes@gmx.de), maybe it can be added to the known firmwares."); + return; + } + + if (!string.IsNullOrEmpty(modificationSettings.LogoFileName)) + { + if (!FirmwareModifier.ChangeLogo(modificationSettings.LogoFileName, firmware, detectedFirmware, out error)) + { + ShowErrorMessage(error); + return; + } + } + + if (modificationSettings.LogoBackgroundColor != Color.Empty) + { + FirmwareModifier.ChangeLogoBackgroundColor(modificationSettings.LogoBackgroundColor, firmware, detectedFirmware); + } + + if (modificationSettings.LogoForegroundColor != Color.Empty) + { + FirmwareModifier.ChangeLogoForegroundColor(modificationSettings.LogoForegroundColor, firmware, detectedFirmware); + } + + if (modificationSettings.BackgroundColor != Color.Empty) + { + FirmwareModifier.ChangeBackgroundColor(modificationSettings.BackgroundColor, firmware, detectedFirmware); + } + + FirmwareModifier.ToggleHdmiPopup(!modificationSettings.RemoveHdmiPopup, firmware, detectedFirmware); + + if (!string.IsNullOrEmpty(modificationSettings.HdmiReplacementText)) + { + FirmwareModifier.ReplaceHdmiPopup(modificationSettings.HdmiReplacementText, firmware, detectedFirmware); + } + + string outputFileName = Path.GetFileNameWithoutExtension(inputFileName) + "_modified.bin"; + + SaveFileDialog saveFileDialog = new SaveFileDialog(); + saveFileDialog.Filter = "Bin files (*.bin)|*.bin"; + saveFileDialog.FileName = outputFileName; + + if (saveFileDialog.ShowDialog() == DialogResult.OK) + { + try + { + File.WriteAllBytes(saveFileDialog.FileName, firmware); + } + catch (Exception ex) + { + ShowErrorMessage($"Could not write file {saveFileDialog.FileName}! {ex.Message}"); + return; + } + } + + MessageBox.Show($"The modified firmware was successfully saved to {saveFileDialog.FileName}.\r\n\r\nYou can now flash it to your RTD266x chip.", "Modify firmware", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } +} diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.resx b/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormFileImage.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/RTD266xFlash/RTD266xFlash/FormFont.Designer.cs b/RTD266xFlash/RTD266xFlash/Forms/FormFont.Designer.cs similarity index 99% rename from RTD266xFlash/RTD266xFlash/FormFont.Designer.cs rename to RTD266xFlash/RTD266xFlash/Forms/FormFont.Designer.cs index 50998fe..6eecbe9 100644 --- a/RTD266xFlash/RTD266xFlash/FormFont.Designer.cs +++ b/RTD266xFlash/RTD266xFlash/Forms/FormFont.Designer.cs @@ -1,4 +1,4 @@ -namespace RTD266xFlash +namespace RTD266xFlash.Forms { partial class FormFont { diff --git a/RTD266xFlash/RTD266xFlash/FormFont.cs b/RTD266xFlash/RTD266xFlash/Forms/FormFont.cs similarity index 99% rename from RTD266xFlash/RTD266xFlash/FormFont.cs rename to RTD266xFlash/RTD266xFlash/Forms/FormFont.cs index d5658bb..8d31598 100644 --- a/RTD266xFlash/RTD266xFlash/FormFont.cs +++ b/RTD266xFlash/RTD266xFlash/Forms/FormFont.cs @@ -3,7 +3,7 @@ using System.IO; using System.Windows.Forms; -namespace RTD266xFlash +namespace RTD266xFlash.Forms { public partial class FormFont : Form { diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormFont.resx b/RTD266xFlash/RTD266xFlash/Forms/FormFont.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormFont.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/RTD266xFlash/RTD266xFlash/Forms/FormStart.Designer.cs b/RTD266xFlash/RTD266xFlash/Forms/FormStart.Designer.cs new file mode 100644 index 0000000..7a8f37b --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormStart.Designer.cs @@ -0,0 +1,171 @@ +namespace RTD266xFlash.Forms +{ + partial class FormStart + { + /// + /// 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() + { + this.lblHint = new System.Windows.Forms.Label(); + this.radioButtonArduino = new System.Windows.Forms.RadioButton(); + this.radioButtonFile = new System.Windows.Forms.RadioButton(); + this.lblHintArduino = new System.Windows.Forms.Label(); + this.lblHintImages = new System.Windows.Forms.Label(); + this.btnStart = new System.Windows.Forms.Button(); + this.btnExit = new System.Windows.Forms.Button(); + this.btnAbout = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lblHint + // + this.lblHint.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lblHint.Location = new System.Drawing.Point(12, 9); + this.lblHint.Name = "lblHint"; + this.lblHint.Size = new System.Drawing.Size(279, 39); + this.lblHint.TabIndex = 0; + this.lblHint.Text = "Please select the way you interface with your RTD266x chip:"; + this.lblHint.DoubleClick += new System.EventHandler(this.lblHint_DoubleClick); + // + // radioButtonArduino + // + this.radioButtonArduino.AutoSize = true; + this.radioButtonArduino.Checked = true; + this.radioButtonArduino.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.radioButtonArduino.Location = new System.Drawing.Point(12, 51); + this.radioButtonArduino.Name = "radioButtonArduino"; + this.radioButtonArduino.Size = new System.Drawing.Size(197, 17); + this.radioButtonArduino.TabIndex = 1; + this.radioButtonArduino.TabStop = true; + this.radioButtonArduino.Text = "Connect directly with an Arduino"; + this.radioButtonArduino.UseVisualStyleBackColor = true; + // + // radioButtonFile + // + this.radioButtonFile.AutoSize = true; + this.radioButtonFile.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.radioButtonFile.Location = new System.Drawing.Point(12, 129); + this.radioButtonFile.Name = "radioButtonFile"; + this.radioButtonFile.Size = new System.Drawing.Size(113, 17); + this.radioButtonFile.TabIndex = 2; + this.radioButtonFile.Text = "Firmware images"; + this.radioButtonFile.UseVisualStyleBackColor = true; + // + // lblHintArduino + // + this.lblHintArduino.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lblHintArduino.Location = new System.Drawing.Point(12, 72); + this.lblHintArduino.Name = "lblHintArduino"; + this.lblHintArduino.Size = new System.Drawing.Size(279, 54); + this.lblHintArduino.TabIndex = 3; + this.lblHintArduino.Text = "Select this option if you are using an Arduino and the RTD266xArduino sketch to c" + + "onnect to the I2C lines using a modified HDMI cable."; + // + // lblHintImages + // + this.lblHintImages.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lblHintImages.Location = new System.Drawing.Point(12, 149); + this.lblHintImages.Name = "lblHintImages"; + this.lblHintImages.Size = new System.Drawing.Size(279, 47); + this.lblHintImages.TabIndex = 4; + this.lblHintImages.Text = "Select this option if you are using the Python script RTD266xPy on a Raspberry Pi" + + " to read and write the firmware via HDMI and use firmware image files."; + // + // btnStart + // + this.btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnStart.Location = new System.Drawing.Point(12, 216); + this.btnStart.Name = "btnStart"; + this.btnStart.Size = new System.Drawing.Size(89, 23); + this.btnStart.TabIndex = 5; + this.btnStart.Text = "Start"; + this.btnStart.UseVisualStyleBackColor = true; + this.btnStart.Click += new System.EventHandler(this.btnStart_Click); + // + // btnExit + // + this.btnExit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnExit.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnExit.Location = new System.Drawing.Point(202, 216); + this.btnExit.Name = "btnExit"; + this.btnExit.Size = new System.Drawing.Size(89, 23); + this.btnExit.TabIndex = 6; + this.btnExit.Text = "Exit"; + this.btnExit.UseVisualStyleBackColor = true; + this.btnExit.Click += new System.EventHandler(this.btnExit_Click); + // + // btnAbout + // + this.btnAbout.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnAbout.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnAbout.Location = new System.Drawing.Point(107, 216); + this.btnAbout.Name = "btnAbout"; + this.btnAbout.Size = new System.Drawing.Size(89, 23); + this.btnAbout.TabIndex = 7; + this.btnAbout.Text = "About..."; + this.btnAbout.UseVisualStyleBackColor = true; + this.btnAbout.Click += new System.EventHandler(this.btnAbout_Click); + // + // FormStart + // + this.AcceptButton = this.btnStart; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnExit; + this.ClientSize = new System.Drawing.Size(303, 251); + this.Controls.Add(this.btnAbout); + this.Controls.Add(this.btnExit); + this.Controls.Add(this.btnStart); + this.Controls.Add(this.lblHintImages); + this.Controls.Add(this.lblHintArduino); + this.Controls.Add(this.radioButtonFile); + this.Controls.Add(this.radioButtonArduino); + this.Controls.Add(this.lblHint); + this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "FormStart"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "RTD266xFlash"; + this.Load += new System.EventHandler(this.FormStart_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lblHint; + private System.Windows.Forms.RadioButton radioButtonArduino; + private System.Windows.Forms.RadioButton radioButtonFile; + private System.Windows.Forms.Label lblHintArduino; + private System.Windows.Forms.Label lblHintImages; + private System.Windows.Forms.Button btnStart; + private System.Windows.Forms.Button btnExit; + private System.Windows.Forms.Button btnAbout; + } +} \ No newline at end of file diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormStart.cs b/RTD266xFlash/RTD266xFlash/Forms/FormStart.cs new file mode 100644 index 0000000..f8e83ad --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormStart.cs @@ -0,0 +1,57 @@ +using System; +using System.Windows.Forms; + +namespace RTD266xFlash.Forms +{ + public partial class FormStart : Form + { + public FormStart() + { + InitializeComponent(); + } + + private void FormStart_Load(object sender, EventArgs e) + { + if (Properties.Settings.Default.FileImageMode) + { + radioButtonFile.Checked = true; + } + } + + private void btnStart_Click(object sender, EventArgs e) + { + Form form; + + if (radioButtonArduino.Checked) + { + form = new FormArduino(); + } + else + { + form = new FormFileImage(); + } + + Properties.Settings.Default.FileImageMode = radioButtonFile.Checked; + Properties.Settings.Default.Save(); + + form.ShowDialog(); + } + + private void btnAbout_Click(object sender, EventArgs e) + { + FormAbout formAbout = new FormAbout(); + formAbout.ShowDialog(); + } + + private void btnExit_Click(object sender, EventArgs e) + { + Close(); + } + + private void lblHint_DoubleClick(object sender, EventArgs e) + { + FormExtras formExtras = new FormExtras(); + formExtras.ShowDialog(); + } + } +} diff --git a/RTD266xFlash/RTD266xFlash/Forms/FormStart.resx b/RTD266xFlash/RTD266xFlash/Forms/FormStart.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/Forms/FormStart.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/RTD266xFlash/RTD266xFlash/ModificationSettings.Designer.cs b/RTD266xFlash/RTD266xFlash/ModificationSettings.Designer.cs new file mode 100644 index 0000000..e00a183 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/ModificationSettings.Designer.cs @@ -0,0 +1,523 @@ +namespace RTD266xFlash +{ + partial class ModificationSettings + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Komponenten-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.picBackgroundColor = new System.Windows.Forms.PictureBox(); + this.picLogoForegroundColor = new System.Windows.Forms.PictureBox(); + this.picLogoBackgroundColor = new System.Windows.Forms.PictureBox(); + this.numericLogoForegroundBlue = new System.Windows.Forms.NumericUpDown(); + this.lblLogoForegroundBlue = new System.Windows.Forms.Label(); + this.numericLogoForegroundGreen = new System.Windows.Forms.NumericUpDown(); + this.lblLogoForegroundGreen = new System.Windows.Forms.Label(); + this.lblLogoForegroundRed = new System.Windows.Forms.Label(); + this.numericLogoForegroundRed = new System.Windows.Forms.NumericUpDown(); + this.chkChangeLogoForegroundColor = new System.Windows.Forms.CheckBox(); + this.txtChangeHdmi = new System.Windows.Forms.TextBox(); + this.chkChangeHdmi = new System.Windows.Forms.CheckBox(); + this.chkRemoveHdmi = new System.Windows.Forms.CheckBox(); + this.numericBackgroundBlue = new System.Windows.Forms.NumericUpDown(); + this.lblBackgroundBlue = new System.Windows.Forms.Label(); + this.numericBackgroundGreen = new System.Windows.Forms.NumericUpDown(); + this.lblBackgroundGreen = new System.Windows.Forms.Label(); + this.lblBackgroundRed = new System.Windows.Forms.Label(); + this.numericBackgroundRed = new System.Windows.Forms.NumericUpDown(); + this.numericLogoBackgroundBlue = new System.Windows.Forms.NumericUpDown(); + this.lblLogoBackgroundBlue = new System.Windows.Forms.Label(); + this.numericLogoBackgroundGreen = new System.Windows.Forms.NumericUpDown(); + this.lblLogoBackgroundGreen = new System.Windows.Forms.Label(); + this.lblLogoBackgroundRed = new System.Windows.Forms.Label(); + this.numericLogoBackgroundRed = new System.Windows.Forms.NumericUpDown(); + this.chkChangeBackgroundColor = new System.Windows.Forms.CheckBox(); + this.chkChangeLogoBackgroundColor = new System.Windows.Forms.CheckBox(); + this.chkChangeLogo = new System.Windows.Forms.CheckBox(); + this.btnModify = new System.Windows.Forms.Button(); + this.btnLogoFileNameBrowse = new System.Windows.Forms.Button(); + this.txtLogoFileName = new System.Windows.Forms.TextBox(); + this.lblLogoFileName = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.picBackgroundColor)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picLogoForegroundColor)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picLogoBackgroundColor)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundBlue)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundGreen)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundRed)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundBlue)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundGreen)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundRed)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundBlue)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundGreen)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundRed)).BeginInit(); + this.SuspendLayout(); + // + // picBackgroundColor + // + this.picBackgroundColor.BackColor = System.Drawing.Color.Black; + this.picBackgroundColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.picBackgroundColor.Location = new System.Drawing.Point(244, 173); + this.picBackgroundColor.Name = "picBackgroundColor"; + this.picBackgroundColor.Size = new System.Drawing.Size(22, 22); + this.picBackgroundColor.TabIndex = 68; + this.picBackgroundColor.TabStop = false; + this.picBackgroundColor.Click += new System.EventHandler(this.picBackgroundColor_Click); + // + // picLogoForegroundColor + // + this.picLogoForegroundColor.BackColor = System.Drawing.Color.White; + this.picLogoForegroundColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.picLogoForegroundColor.Location = new System.Drawing.Point(244, 122); + this.picLogoForegroundColor.Name = "picLogoForegroundColor"; + this.picLogoForegroundColor.Size = new System.Drawing.Size(22, 22); + this.picLogoForegroundColor.TabIndex = 67; + this.picLogoForegroundColor.TabStop = false; + this.picLogoForegroundColor.Click += new System.EventHandler(this.picLogoForegroundColor_Click); + // + // picLogoBackgroundColor + // + this.picLogoBackgroundColor.BackColor = System.Drawing.Color.Black; + this.picLogoBackgroundColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.picLogoBackgroundColor.Location = new System.Drawing.Point(244, 71); + this.picLogoBackgroundColor.Name = "picLogoBackgroundColor"; + this.picLogoBackgroundColor.Size = new System.Drawing.Size(22, 22); + this.picLogoBackgroundColor.TabIndex = 66; + this.picLogoBackgroundColor.TabStop = false; + this.picLogoBackgroundColor.Click += new System.EventHandler(this.picLogoBackgroundColor_Click); + // + // numericLogoForegroundBlue + // + this.numericLogoForegroundBlue.Location = new System.Drawing.Point(186, 122); + this.numericLogoForegroundBlue.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoForegroundBlue.Name = "numericLogoForegroundBlue"; + this.numericLogoForegroundBlue.Size = new System.Drawing.Size(52, 20); + this.numericLogoForegroundBlue.TabIndex = 65; + this.numericLogoForegroundBlue.Value = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoForegroundBlue.ValueChanged += new System.EventHandler(this.numericLogoForeground_ValueChanged); + // + // lblLogoForegroundBlue + // + this.lblLogoForegroundBlue.AutoSize = true; + this.lblLogoForegroundBlue.Location = new System.Drawing.Point(163, 124); + this.lblLogoForegroundBlue.Name = "lblLogoForegroundBlue"; + this.lblLogoForegroundBlue.Size = new System.Drawing.Size(17, 13); + this.lblLogoForegroundBlue.TabIndex = 64; + this.lblLogoForegroundBlue.Text = "B:"; + // + // numericLogoForegroundGreen + // + this.numericLogoForegroundGreen.Location = new System.Drawing.Point(105, 122); + this.numericLogoForegroundGreen.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoForegroundGreen.Name = "numericLogoForegroundGreen"; + this.numericLogoForegroundGreen.Size = new System.Drawing.Size(52, 20); + this.numericLogoForegroundGreen.TabIndex = 63; + this.numericLogoForegroundGreen.Value = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoForegroundGreen.ValueChanged += new System.EventHandler(this.numericLogoForeground_ValueChanged); + // + // lblLogoForegroundGreen + // + this.lblLogoForegroundGreen.AutoSize = true; + this.lblLogoForegroundGreen.Location = new System.Drawing.Point(81, 124); + this.lblLogoForegroundGreen.Name = "lblLogoForegroundGreen"; + this.lblLogoForegroundGreen.Size = new System.Drawing.Size(18, 13); + this.lblLogoForegroundGreen.TabIndex = 62; + this.lblLogoForegroundGreen.Text = "G:"; + // + // lblLogoForegroundRed + // + this.lblLogoForegroundRed.AutoSize = true; + this.lblLogoForegroundRed.Location = new System.Drawing.Point(0, 124); + this.lblLogoForegroundRed.Name = "lblLogoForegroundRed"; + this.lblLogoForegroundRed.Size = new System.Drawing.Size(18, 13); + this.lblLogoForegroundRed.TabIndex = 61; + this.lblLogoForegroundRed.Text = "R:"; + // + // numericLogoForegroundRed + // + this.numericLogoForegroundRed.Location = new System.Drawing.Point(23, 122); + this.numericLogoForegroundRed.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoForegroundRed.Name = "numericLogoForegroundRed"; + this.numericLogoForegroundRed.Size = new System.Drawing.Size(52, 20); + this.numericLogoForegroundRed.TabIndex = 60; + this.numericLogoForegroundRed.Value = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoForegroundRed.ValueChanged += new System.EventHandler(this.numericLogoForeground_ValueChanged); + // + // chkChangeLogoForegroundColor + // + this.chkChangeLogoForegroundColor.AutoSize = true; + this.chkChangeLogoForegroundColor.Location = new System.Drawing.Point(3, 99); + this.chkChangeLogoForegroundColor.Name = "chkChangeLogoForegroundColor"; + this.chkChangeLogoForegroundColor.Size = new System.Drawing.Size(166, 17); + this.chkChangeLogoForegroundColor.TabIndex = 59; + this.chkChangeLogoForegroundColor.Text = "Change logo foreground color"; + this.chkChangeLogoForegroundColor.UseVisualStyleBackColor = true; + this.chkChangeLogoForegroundColor.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); + // + // txtChangeHdmi + // + this.txtChangeHdmi.Location = new System.Drawing.Point(174, 222); + this.txtChangeHdmi.Name = "txtChangeHdmi"; + this.txtChangeHdmi.Size = new System.Drawing.Size(113, 20); + this.txtChangeHdmi.TabIndex = 58; + this.txtChangeHdmi.Text = "HDMI"; + // + // chkChangeHdmi + // + this.chkChangeHdmi.AutoSize = true; + this.chkChangeHdmi.Location = new System.Drawing.Point(3, 224); + this.chkChangeHdmi.Name = "chkChangeHdmi"; + this.chkChangeHdmi.Size = new System.Drawing.Size(155, 17); + this.chkChangeHdmi.TabIndex = 57; + this.chkChangeHdmi.Text = "Change \"HDMI\" pop-up to:"; + this.chkChangeHdmi.UseVisualStyleBackColor = true; + this.chkChangeHdmi.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); + // + // chkRemoveHdmi + // + this.chkRemoveHdmi.AutoSize = true; + this.chkRemoveHdmi.Location = new System.Drawing.Point(3, 201); + this.chkRemoveHdmi.Name = "chkRemoveHdmi"; + this.chkRemoveHdmi.Size = new System.Drawing.Size(143, 17); + this.chkRemoveHdmi.TabIndex = 56; + this.chkRemoveHdmi.Text = "Remove \"HDMI\" pop-up"; + this.chkRemoveHdmi.UseVisualStyleBackColor = true; + this.chkRemoveHdmi.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); + // + // numericBackgroundBlue + // + this.numericBackgroundBlue.Location = new System.Drawing.Point(186, 173); + this.numericBackgroundBlue.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericBackgroundBlue.Name = "numericBackgroundBlue"; + this.numericBackgroundBlue.Size = new System.Drawing.Size(52, 20); + this.numericBackgroundBlue.TabIndex = 55; + this.numericBackgroundBlue.ValueChanged += new System.EventHandler(this.numericBackground_ValueChanged); + // + // lblBackgroundBlue + // + this.lblBackgroundBlue.AutoSize = true; + this.lblBackgroundBlue.Location = new System.Drawing.Point(163, 175); + this.lblBackgroundBlue.Name = "lblBackgroundBlue"; + this.lblBackgroundBlue.Size = new System.Drawing.Size(17, 13); + this.lblBackgroundBlue.TabIndex = 54; + this.lblBackgroundBlue.Text = "B:"; + // + // numericBackgroundGreen + // + this.numericBackgroundGreen.Location = new System.Drawing.Point(105, 173); + this.numericBackgroundGreen.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericBackgroundGreen.Name = "numericBackgroundGreen"; + this.numericBackgroundGreen.Size = new System.Drawing.Size(52, 20); + this.numericBackgroundGreen.TabIndex = 53; + this.numericBackgroundGreen.ValueChanged += new System.EventHandler(this.numericBackground_ValueChanged); + // + // lblBackgroundGreen + // + this.lblBackgroundGreen.AutoSize = true; + this.lblBackgroundGreen.Location = new System.Drawing.Point(81, 175); + this.lblBackgroundGreen.Name = "lblBackgroundGreen"; + this.lblBackgroundGreen.Size = new System.Drawing.Size(18, 13); + this.lblBackgroundGreen.TabIndex = 52; + this.lblBackgroundGreen.Text = "G:"; + // + // lblBackgroundRed + // + this.lblBackgroundRed.AutoSize = true; + this.lblBackgroundRed.Location = new System.Drawing.Point(0, 175); + this.lblBackgroundRed.Name = "lblBackgroundRed"; + this.lblBackgroundRed.Size = new System.Drawing.Size(18, 13); + this.lblBackgroundRed.TabIndex = 51; + this.lblBackgroundRed.Text = "R:"; + // + // numericBackgroundRed + // + this.numericBackgroundRed.Location = new System.Drawing.Point(23, 173); + this.numericBackgroundRed.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericBackgroundRed.Name = "numericBackgroundRed"; + this.numericBackgroundRed.Size = new System.Drawing.Size(52, 20); + this.numericBackgroundRed.TabIndex = 50; + this.numericBackgroundRed.ValueChanged += new System.EventHandler(this.numericBackground_ValueChanged); + // + // numericLogoBackgroundBlue + // + this.numericLogoBackgroundBlue.Location = new System.Drawing.Point(186, 71); + this.numericLogoBackgroundBlue.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoBackgroundBlue.Name = "numericLogoBackgroundBlue"; + this.numericLogoBackgroundBlue.Size = new System.Drawing.Size(52, 20); + this.numericLogoBackgroundBlue.TabIndex = 49; + this.numericLogoBackgroundBlue.ValueChanged += new System.EventHandler(this.numericLogoBackground_ValueChanged); + // + // lblLogoBackgroundBlue + // + this.lblLogoBackgroundBlue.AutoSize = true; + this.lblLogoBackgroundBlue.Location = new System.Drawing.Point(163, 73); + this.lblLogoBackgroundBlue.Name = "lblLogoBackgroundBlue"; + this.lblLogoBackgroundBlue.Size = new System.Drawing.Size(17, 13); + this.lblLogoBackgroundBlue.TabIndex = 48; + this.lblLogoBackgroundBlue.Text = "B:"; + // + // numericLogoBackgroundGreen + // + this.numericLogoBackgroundGreen.Location = new System.Drawing.Point(105, 71); + this.numericLogoBackgroundGreen.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoBackgroundGreen.Name = "numericLogoBackgroundGreen"; + this.numericLogoBackgroundGreen.Size = new System.Drawing.Size(52, 20); + this.numericLogoBackgroundGreen.TabIndex = 47; + this.numericLogoBackgroundGreen.ValueChanged += new System.EventHandler(this.numericLogoBackground_ValueChanged); + // + // lblLogoBackgroundGreen + // + this.lblLogoBackgroundGreen.AutoSize = true; + this.lblLogoBackgroundGreen.Location = new System.Drawing.Point(81, 73); + this.lblLogoBackgroundGreen.Name = "lblLogoBackgroundGreen"; + this.lblLogoBackgroundGreen.Size = new System.Drawing.Size(18, 13); + this.lblLogoBackgroundGreen.TabIndex = 46; + this.lblLogoBackgroundGreen.Text = "G:"; + // + // lblLogoBackgroundRed + // + this.lblLogoBackgroundRed.AutoSize = true; + this.lblLogoBackgroundRed.Location = new System.Drawing.Point(0, 73); + this.lblLogoBackgroundRed.Name = "lblLogoBackgroundRed"; + this.lblLogoBackgroundRed.Size = new System.Drawing.Size(18, 13); + this.lblLogoBackgroundRed.TabIndex = 45; + this.lblLogoBackgroundRed.Text = "R:"; + // + // numericLogoBackgroundRed + // + this.numericLogoBackgroundRed.Location = new System.Drawing.Point(23, 71); + this.numericLogoBackgroundRed.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericLogoBackgroundRed.Name = "numericLogoBackgroundRed"; + this.numericLogoBackgroundRed.Size = new System.Drawing.Size(52, 20); + this.numericLogoBackgroundRed.TabIndex = 44; + this.numericLogoBackgroundRed.ValueChanged += new System.EventHandler(this.numericLogoBackground_ValueChanged); + // + // chkChangeBackgroundColor + // + this.chkChangeBackgroundColor.AutoSize = true; + this.chkChangeBackgroundColor.Location = new System.Drawing.Point(3, 150); + this.chkChangeBackgroundColor.Name = "chkChangeBackgroundColor"; + this.chkChangeBackgroundColor.Size = new System.Drawing.Size(184, 17); + this.chkChangeBackgroundColor.TabIndex = 43; + this.chkChangeBackgroundColor.Text = "Change display background color"; + this.chkChangeBackgroundColor.UseVisualStyleBackColor = true; + this.chkChangeBackgroundColor.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); + // + // chkChangeLogoBackgroundColor + // + this.chkChangeLogoBackgroundColor.AutoSize = true; + this.chkChangeLogoBackgroundColor.Location = new System.Drawing.Point(3, 48); + this.chkChangeLogoBackgroundColor.Name = "chkChangeLogoBackgroundColor"; + this.chkChangeLogoBackgroundColor.Size = new System.Drawing.Size(172, 17); + this.chkChangeLogoBackgroundColor.TabIndex = 42; + this.chkChangeLogoBackgroundColor.Text = "Change logo background color"; + this.chkChangeLogoBackgroundColor.UseVisualStyleBackColor = true; + this.chkChangeLogoBackgroundColor.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); + // + // chkChangeLogo + // + this.chkChangeLogo.AutoSize = true; + this.chkChangeLogo.Location = new System.Drawing.Point(3, 3); + this.chkChangeLogo.Name = "chkChangeLogo"; + this.chkChangeLogo.Size = new System.Drawing.Size(86, 17); + this.chkChangeLogo.TabIndex = 41; + this.chkChangeLogo.Text = "Change logo"; + this.chkChangeLogo.UseVisualStyleBackColor = true; + this.chkChangeLogo.CheckedChanged += new System.EventHandler(this.chkModifyFirmware_CheckedChanged); + // + // btnModify + // + this.btnModify.Location = new System.Drawing.Point(3, 247); + this.btnModify.Name = "btnModify"; + this.btnModify.Size = new System.Drawing.Size(114, 23); + this.btnModify.TabIndex = 40; + this.btnModify.Text = "Modify firmware"; + this.btnModify.UseVisualStyleBackColor = true; + this.btnModify.Click += new System.EventHandler(this.btnModify_Click); + // + // btnLogoFileNameBrowse + // + this.btnLogoFileNameBrowse.Location = new System.Drawing.Point(256, 20); + this.btnLogoFileNameBrowse.Name = "btnLogoFileNameBrowse"; + this.btnLogoFileNameBrowse.Size = new System.Drawing.Size(31, 22); + this.btnLogoFileNameBrowse.TabIndex = 39; + this.btnLogoFileNameBrowse.Text = "..."; + this.btnLogoFileNameBrowse.UseVisualStyleBackColor = true; + this.btnLogoFileNameBrowse.Click += new System.EventHandler(this.btnLogoFileNameBrowse_Click); + // + // txtLogoFileName + // + this.txtLogoFileName.Location = new System.Drawing.Point(92, 20); + this.txtLogoFileName.Name = "txtLogoFileName"; + this.txtLogoFileName.Size = new System.Drawing.Size(158, 20); + this.txtLogoFileName.TabIndex = 38; + // + // lblLogoFileName + // + this.lblLogoFileName.AutoSize = true; + this.lblLogoFileName.Location = new System.Drawing.Point(0, 23); + this.lblLogoFileName.Name = "lblLogoFileName"; + this.lblLogoFileName.Size = new System.Drawing.Size(76, 13); + this.lblLogoFileName.TabIndex = 37; + this.lblLogoFileName.Text = "Logo input file:"; + // + // ModificationSettings + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.picBackgroundColor); + this.Controls.Add(this.picLogoForegroundColor); + this.Controls.Add(this.picLogoBackgroundColor); + this.Controls.Add(this.numericLogoForegroundBlue); + this.Controls.Add(this.lblLogoForegroundBlue); + this.Controls.Add(this.numericLogoForegroundGreen); + this.Controls.Add(this.lblLogoForegroundGreen); + this.Controls.Add(this.lblLogoForegroundRed); + this.Controls.Add(this.numericLogoForegroundRed); + this.Controls.Add(this.chkChangeLogoForegroundColor); + this.Controls.Add(this.txtChangeHdmi); + this.Controls.Add(this.chkChangeHdmi); + this.Controls.Add(this.chkRemoveHdmi); + this.Controls.Add(this.numericBackgroundBlue); + this.Controls.Add(this.lblBackgroundBlue); + this.Controls.Add(this.numericBackgroundGreen); + this.Controls.Add(this.lblBackgroundGreen); + this.Controls.Add(this.lblBackgroundRed); + this.Controls.Add(this.numericBackgroundRed); + this.Controls.Add(this.numericLogoBackgroundBlue); + this.Controls.Add(this.lblLogoBackgroundBlue); + this.Controls.Add(this.numericLogoBackgroundGreen); + this.Controls.Add(this.lblLogoBackgroundGreen); + this.Controls.Add(this.lblLogoBackgroundRed); + this.Controls.Add(this.numericLogoBackgroundRed); + this.Controls.Add(this.chkChangeBackgroundColor); + this.Controls.Add(this.chkChangeLogoBackgroundColor); + this.Controls.Add(this.chkChangeLogo); + this.Controls.Add(this.btnModify); + this.Controls.Add(this.btnLogoFileNameBrowse); + this.Controls.Add(this.txtLogoFileName); + this.Controls.Add(this.lblLogoFileName); + this.Name = "ModificationSettings"; + this.Size = new System.Drawing.Size(291, 275); + ((System.ComponentModel.ISupportInitialize)(this.picBackgroundColor)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picLogoForegroundColor)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picLogoBackgroundColor)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundBlue)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundGreen)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoForegroundRed)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundBlue)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundGreen)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericBackgroundRed)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundBlue)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundGreen)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericLogoBackgroundRed)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.PictureBox picBackgroundColor; + private System.Windows.Forms.PictureBox picLogoForegroundColor; + private System.Windows.Forms.PictureBox picLogoBackgroundColor; + private System.Windows.Forms.NumericUpDown numericLogoForegroundBlue; + private System.Windows.Forms.Label lblLogoForegroundBlue; + private System.Windows.Forms.NumericUpDown numericLogoForegroundGreen; + private System.Windows.Forms.Label lblLogoForegroundGreen; + private System.Windows.Forms.Label lblLogoForegroundRed; + private System.Windows.Forms.NumericUpDown numericLogoForegroundRed; + private System.Windows.Forms.CheckBox chkChangeLogoForegroundColor; + private System.Windows.Forms.TextBox txtChangeHdmi; + private System.Windows.Forms.CheckBox chkChangeHdmi; + private System.Windows.Forms.CheckBox chkRemoveHdmi; + private System.Windows.Forms.NumericUpDown numericBackgroundBlue; + private System.Windows.Forms.Label lblBackgroundBlue; + private System.Windows.Forms.NumericUpDown numericBackgroundGreen; + private System.Windows.Forms.Label lblBackgroundGreen; + private System.Windows.Forms.Label lblBackgroundRed; + private System.Windows.Forms.NumericUpDown numericBackgroundRed; + private System.Windows.Forms.NumericUpDown numericLogoBackgroundBlue; + private System.Windows.Forms.Label lblLogoBackgroundBlue; + private System.Windows.Forms.NumericUpDown numericLogoBackgroundGreen; + private System.Windows.Forms.Label lblLogoBackgroundGreen; + private System.Windows.Forms.Label lblLogoBackgroundRed; + private System.Windows.Forms.NumericUpDown numericLogoBackgroundRed; + private System.Windows.Forms.CheckBox chkChangeBackgroundColor; + private System.Windows.Forms.CheckBox chkChangeLogoBackgroundColor; + private System.Windows.Forms.CheckBox chkChangeLogo; + private System.Windows.Forms.Button btnModify; + private System.Windows.Forms.Button btnLogoFileNameBrowse; + private System.Windows.Forms.TextBox txtLogoFileName; + private System.Windows.Forms.Label lblLogoFileName; + } +} diff --git a/RTD266xFlash/RTD266xFlash/ModificationSettings.cs b/RTD266xFlash/RTD266xFlash/ModificationSettings.cs new file mode 100644 index 0000000..699eb40 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/ModificationSettings.cs @@ -0,0 +1,276 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace RTD266xFlash +{ + public partial class ModificationSettings : UserControl + { + #region Events + + public delegate void ModifyClickedHandler(); + + /// + /// Modify button clicked event + /// + public event ModifyClickedHandler ModifyClickedEvent; + + #endregion + + #region Members + + private bool _guiUpdate; + + #endregion + + #region Properties + + /// + /// Enabled state of modify button + /// + public bool ModifyEnabled + { + get + { + return btnModify.Enabled; + } + set + { + btnModify.Enabled = value; + } + } + + /// + /// Logo file name or null if none selected + /// + public string LogoFileName + { + get + { + if (chkChangeLogo.Checked) + { + return txtLogoFileName.Text; + } + + return null; + } + } + + /// + /// Selected logo background color or empty if none selected + /// + public Color LogoBackgroundColor + { + get + { + if (chkChangeLogoBackgroundColor.Checked) + { + return Color.FromArgb((int)numericLogoBackgroundRed.Value, (int)numericLogoBackgroundGreen.Value, (int)numericLogoBackgroundBlue.Value); + } + + return Color.Empty; + } + } + + /// + /// Selected logo foreground color or empty if none selected + /// + public Color LogoForegroundColor + { + get + { + if (chkChangeLogoForegroundColor.Checked) + { + return Color.FromArgb((int)numericLogoForegroundRed.Value, (int)numericLogoForegroundGreen.Value, (int)numericLogoForegroundBlue.Value); + } + + return Color.Empty; + } + } + + /// + /// Selected background color or empty if none selected + /// + public Color BackgroundColor + { + get + { + if (chkChangeBackgroundColor.Checked) + { + return Color.FromArgb((int)numericBackgroundRed.Value, (int)numericBackgroundGreen.Value, (int)numericBackgroundBlue.Value); + } + + return Color.Empty; + } + } + + /// + /// Remove "HDMI" popup + /// + public bool RemoveHdmiPopup + { + get + { + return chkRemoveHdmi.Checked; + } + } + + /// + /// Replacement text for "HDMI" popup + /// + public string HdmiReplacementText + { + get + { + if (chkChangeHdmi.Checked) + { + return txtChangeHdmi.Text; + } + + return null; + } + } + + #endregion + + public ModificationSettings() + { + InitializeComponent(); + } + + public void UpdateModifyFirmware() + { + _guiUpdate = true; + + txtLogoFileName.Enabled = chkChangeLogo.Checked; + btnLogoFileNameBrowse.Enabled = chkChangeLogo.Checked; + + numericLogoBackgroundRed.Enabled = chkChangeLogoBackgroundColor.Checked; + numericLogoBackgroundGreen.Enabled = chkChangeLogoBackgroundColor.Checked; + numericLogoBackgroundBlue.Enabled = chkChangeLogoBackgroundColor.Checked; + picLogoBackgroundColor.Enabled = chkChangeLogoBackgroundColor.Checked; + + numericLogoForegroundRed.Enabled = chkChangeLogoForegroundColor.Checked; + numericLogoForegroundGreen.Enabled = chkChangeLogoForegroundColor.Checked; + numericLogoForegroundBlue.Enabled = chkChangeLogoForegroundColor.Checked; + picLogoForegroundColor.Enabled = chkChangeLogoForegroundColor.Checked; + + numericBackgroundRed.Enabled = chkChangeBackgroundColor.Checked; + numericBackgroundGreen.Enabled = chkChangeBackgroundColor.Checked; + numericBackgroundBlue.Enabled = chkChangeBackgroundColor.Checked; + picBackgroundColor.Enabled = chkChangeBackgroundColor.Checked; + + if (chkRemoveHdmi.Checked) + { + chkChangeHdmi.Checked = false; + chkChangeHdmi.Enabled = false; + } + else + { + chkChangeHdmi.Enabled = true; + } + + if (chkChangeHdmi.Checked) + { + chkRemoveHdmi.Checked = false; + chkRemoveHdmi.Enabled = false; + } + else + { + chkRemoveHdmi.Enabled = true; + } + + txtChangeHdmi.Enabled = chkChangeHdmi.Checked; + + _guiUpdate = false; + } + + private void FillColorBox(PictureBox pictureBox, int red, int green, int blue) + { + pictureBox.BackColor = Color.FromArgb(red, green, blue); + } + + private void ShowColorDialog(NumericUpDown numericRed, NumericUpDown numericGreen, NumericUpDown numericBlue) + { + ColorDialog colorDialog = new ColorDialog(); + + colorDialog.Color = Color.FromArgb((int)numericRed.Value, (int)numericGreen.Value, (int)numericBlue.Value); + + if (colorDialog.ShowDialog() == DialogResult.OK) + { + numericRed.Value = colorDialog.Color.R; + numericGreen.Value = colorDialog.Color.G; + numericBlue.Value = colorDialog.Color.B; + } + } + + private void btnLogoFileNameBrowse_Click(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "Images (*.png, *.bmp, *.tif, *.tiff)|*.png;*.bmp;*.tif;*.tiff"; + + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + string error; + + if (!FontCoder.CheckFile(openFileDialog.FileName, FontCoder.FontWidthKedei, FontCoder.FontHeightKedei, out error)) + { + MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } + else + { + txtLogoFileName.Text = openFileDialog.FileName; + } + } + } + + #region GUI events + + private void picLogoBackgroundColor_Click(object sender, EventArgs e) + { + ShowColorDialog(numericLogoBackgroundRed, numericLogoBackgroundGreen, numericLogoBackgroundBlue); + } + + private void picLogoForegroundColor_Click(object sender, EventArgs e) + { + ShowColorDialog(numericLogoForegroundRed, numericLogoForegroundGreen, numericLogoForegroundBlue); + } + + private void picBackgroundColor_Click(object sender, EventArgs e) + { + ShowColorDialog(numericBackgroundRed, numericBackgroundGreen, numericBackgroundBlue); + } + + private void numericLogoBackground_ValueChanged(object sender, EventArgs e) + { + FillColorBox(picLogoBackgroundColor, (int)numericLogoBackgroundRed.Value, (int)numericLogoBackgroundGreen.Value, (int)numericLogoBackgroundBlue.Value); + } + + private void numericLogoForeground_ValueChanged(object sender, EventArgs e) + { + FillColorBox(picLogoForegroundColor, (int)numericLogoForegroundRed.Value, (int)numericLogoForegroundGreen.Value, (int)numericLogoForegroundBlue.Value); + } + + private void numericBackground_ValueChanged(object sender, EventArgs e) + { + FillColorBox(picBackgroundColor, (int)numericBackgroundRed.Value, (int)numericBackgroundGreen.Value, (int)numericBackgroundBlue.Value); + } + + private void btnModify_Click(object sender, EventArgs e) + { + ModifyClickedEvent?.Invoke(); + } + + private void chkModifyFirmware_CheckedChanged(object sender, EventArgs e) + { + if (_guiUpdate) + { + return; + } + + UpdateModifyFirmware(); + } + + #endregion + } +} diff --git a/RTD266xFlash/RTD266xFlash/ModificationSettings.resx b/RTD266xFlash/RTD266xFlash/ModificationSettings.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RTD266xFlash/RTD266xFlash/ModificationSettings.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/RTD266xFlash/RTD266xFlash/Program.cs b/RTD266xFlash/RTD266xFlash/Program.cs index 7b21e06..fdd4c9a 100644 --- a/RTD266xFlash/RTD266xFlash/Program.cs +++ b/RTD266xFlash/RTD266xFlash/Program.cs @@ -1,19 +1,20 @@ using System; using System.Windows.Forms; +using RTD266xFlash.Forms; namespace RTD266xFlash { - static class Program + public static class Program { /// /// Der Haupteinstiegspunkt für die Anwendung. /// [STAThread] - static void Main() + public static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new FormMain()); + Application.Run(new FormStart()); } } } diff --git a/RTD266xFlash/RTD266xFlash/Properties/AssemblyInfo.cs b/RTD266xFlash/RTD266xFlash/Properties/AssemblyInfo.cs index e2a44fb..abe0c50 100644 --- a/RTD266xFlash/RTD266xFlash/Properties/AssemblyInfo.cs +++ b/RTD266xFlash/RTD266xFlash/Properties/AssemblyInfo.cs @@ -4,11 +4,11 @@ // Allgemeine Informationen über eine Assembly werden über die folgenden // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("RTD2660Flash")] +[assembly: AssemblyTitle("RTD266xFlash")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("RTD2660Flash")] +[assembly: AssemblyProduct("RTD266xFlash")] [assembly: AssemblyCopyright("Copyright © 2018-2019 floppes")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,5 +31,5 @@ // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.6.0.0")] -[assembly: AssemblyFileVersion("1.6.0.0")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/RTD266xFlash/RTD266xFlash/Properties/Settings.Designer.cs b/RTD266xFlash/RTD266xFlash/Properties/Settings.Designer.cs index e81b260..7761f17 100644 --- a/RTD266xFlash/RTD266xFlash/Properties/Settings.Designer.cs +++ b/RTD266xFlash/RTD266xFlash/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace RTD266xFlash.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -34,5 +34,17 @@ public bool ExpertMode { this["ExpertMode"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool FileImageMode { + get { + return ((bool)(this["FileImageMode"])); + } + set { + this["FileImageMode"] = value; + } + } } } diff --git a/RTD266xFlash/RTD266xFlash/Properties/Settings.settings b/RTD266xFlash/RTD266xFlash/Properties/Settings.settings index cfa4835..98f506b 100644 --- a/RTD266xFlash/RTD266xFlash/Properties/Settings.settings +++ b/RTD266xFlash/RTD266xFlash/Properties/Settings.settings @@ -1,9 +1,12 @@  - + False + + False + \ No newline at end of file diff --git a/RTD266xFlash/RTD266xFlash/RTD266xFlash.csproj b/RTD266xFlash/RTD266xFlash/RTD266xFlash.csproj index b8a9561..929e1eb 100644 --- a/RTD266xFlash/RTD266xFlash/RTD266xFlash.csproj +++ b/RTD266xFlash/RTD266xFlash/RTD266xFlash.csproj @@ -40,6 +40,7 @@ + @@ -47,39 +48,76 @@ + - + Form - + FormAbout.cs - + Form - + + FormExtras.cs + + + Form + + + FormFileImage.cs + + + Form + + FormFont.cs - + Form - - FormMain.cs + + FormArduino.cs + + + Form + + + FormStart.cs + + UserControl + + + ModificationSettings.cs + - + FormAbout.cs - + + FormExtras.cs + + + FormFileImage.cs + + FormFont.cs - - FormMain.cs + + FormArduino.cs + + + FormStart.cs + + + ModificationSettings.cs ResXFileCodeGenerator diff --git a/RTD266xPy/rtd266x/__init__.py b/RTD266xPy/rtd266x/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/RTD266xPy/rtd266x/crc.py b/RTD266xPy/rtd266x/crc.py new file mode 100644 index 0000000..a8a4eb4 --- /dev/null +++ b/RTD266xPy/rtd266x/crc.py @@ -0,0 +1,16 @@ +class CRC(): + def __init__(self): + self.crc = 0 + + def process(self, data): + for data_byte in data: + self.crc ^= data_byte << 8 + + for i in range(8): + if (self.crc & 0x8000): + self.crc ^= 0x1070 << 3 + + self.crc <<= 1 + + def get_crc(self): + return (self.crc >> 8) \ No newline at end of file diff --git a/RTD266xPy/rtd266x/rtd266x.py b/RTD266xPy/rtd266x/rtd266x.py new file mode 100644 index 0000000..b7cac11 --- /dev/null +++ b/RTD266xPy/rtd266x/rtd266x.py @@ -0,0 +1,317 @@ +from crc import CRC +from smbus import SMBus +import time + +class RTD266x(): + SPI_CMD_NOOP = 0 + SPI_CMD_WRITE = 1 + SPI_CMD_READ = 2 + SPI_CMD_WRITE_AFTER_WREN = 3 + SPI_CMD_WRITE_AFTER_EWSR = 4 + SPI_CMD_ERASE = 5 + + # time for register reading + TIMEOUT = 0.1 + + # flash sector size + SECTOR_SIZE = 4096 + + # segment size + SEGMENT_SIZE = 256 + + def __init__(self, interface, address): + self.addr = address + self.i2c = SMBus(interface) + + def _i2c_read_reg(self, reg): + return self.i2c.read_byte_data(self.addr, reg) + + def _i2c_write_reg(self, reg, value): + self.i2c.write_byte_data(self.addr, reg, value) + + def _i2c_read_bytes(self, reg, len): + data = self.i2c.read_i2c_block_data(self.addr, reg) + + return data[0:len] + + def _i2c_write_bytes(self, reg, values): + self.i2c.write_i2c_block_data(self.addr, reg, values) + + def _wait_for_reg_bit(self, reg, mask, expected): + start = time.time() + + while (True): + value = self._i2c_read_reg(reg) + + if ((value & mask) == expected): + return True + + if (time.time() - start > self.TIMEOUT): + print "Timeout when waiting for register " + hex(reg) + "!" + return False + + def _wait_write_done(self): + return self._wait_for_reg_bit(0x6F, 0x40, 0x00) + + def _should_program_page(self, data): + for data_byte in data: + if (data_byte != 0xFF): + return True + + return False + + def spi_common_command(self, cmd, cmd_code, num_reads, num_writes, write_value): + num_reads &= 0x03 + num_writes &= 0x03 + write_value &= 0xFFFFFF + reg_value = (cmd << 5) | (num_writes << 3) | (num_reads << 1) + + self._i2c_write_reg(0x60, reg_value) + self._i2c_write_reg(0x61, cmd_code) + + if (num_writes == 3): + self._i2c_write_reg(0x64, (write_value >> 16) & 0xFF) + self._i2c_write_reg(0x65, (write_value >> 8) & 0xFF) + self._i2c_write_reg(0x66, (write_value >> 0) & 0xFF) + + if (num_writes == 2): + self._i2c_write_reg(0x64, (write_value >> 8) & 0xFF) + self._i2c_write_reg(0x65, (write_value >> 0) & 0xFF) + + if (num_writes == 1): + self._i2c_write_reg(0x64, (write_value >> 0) & 0xFF) + + # execute the command + self._i2c_write_reg(0x60, reg_value | 0x01); + + if (not self._wait_for_reg_bit(0x60, 0x01, 0x00)): + return 0 + + if (num_reads == 0): + return 0 + + if (num_reads == 1): + return self._i2c_read_reg(0x67) + + if (num_reads == 2): + return (self._i2c_read_reg(0x67) << 8) | self._i2c_read_reg(0x68) + + if (num_reads == 3): + return (self._i2c_read_reg(0x67) << 16) | (self._i2c_read_reg(0x68) << 8) | self._i2c_read_reg(0x69) + + return 0 + + def spi_compute_crc(self, start, end): + self._i2c_write_reg(0x64, (start >> 16) & 0xFF) + self._i2c_write_reg(0x65, (start >> 8) & 0xFF) + self._i2c_write_reg(0x66, (start >> 0) & 0xFF) + + self._i2c_write_reg(0x72, (end >> 16) & 0xFF) + self._i2c_write_reg(0x73, (end >> 8) & 0xFF) + self._i2c_write_reg(0x74, (end >> 0) & 0xFF) + + self._i2c_write_reg(0x6F, 0x84) + + if (not self._wait_for_reg_bit(0x6F, 0x02, 0x02)): + return 0 + + return self._i2c_read_reg(0x75) + + def spi_read(self, address, len): + self._i2c_write_reg(0x60, 0x46) + self._i2c_write_reg(0x61, 0x03) + self._i2c_write_reg(0x64, (address >> 16) & 0xFF) + self._i2c_write_reg(0x65, (address >> 8) & 0xFF) + self._i2c_write_reg(0x66, (address >> 0) & 0xFF) + + # execute the command + self._i2c_write_reg(0x60, 0x47) + + if (not self._wait_for_reg_bit(0x60, 0x01, 0x00)): + return [] + + data = [] + + while (len > 0): + read_len = len + + if (read_len > 32): + read_len = 32 + + data += self._i2c_read_bytes(0x70, read_len) + len -= read_len + address += read_len + + return data + + def reset(self): + self._i2c_write_reg(0x6F, 0x01) + + time.sleep(2) + + self._i2c_write_reg(0x6F, 0x00) + + def enter_isp_mode(self): + self._i2c_write_reg(0x6F, 0x80) + + if (not self._wait_for_reg_bit(0x6F, 0x80, 0x80)): + return False + + return True + + def read_id(self): + return self.spi_common_command(self.SPI_CMD_READ, 0x90, 2, 3, 0) + + def read_jedec_id(self): + return self.spi_common_command(self.SPI_CMD_READ, 0x9F, 3, 0, 0) + + def read_status(self): + return (self.spi_common_command(self.SPI_CMD_READ, 0x35, 1, 0, 0) << 8) + (self.spi_common_command(self.SPI_CMD_READ, 0x05, 1, 0, 0)) + + def write_status(self, status): + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_WREN, 0x01, 0, 2, status) + + def erase_chip(self): + # unprotect the status register + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_EWSR, 0x01, 0, 1, 0) + + # unprotect the flash + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_WREN, 0x01, 0, 1, 0) + + # erase chip + self.spi_common_command(self.SPI_CMD_ERASE, 0xC7, 0, 0, 0) + + return True + + def erase_sector(self, address): + # unprotect the status register + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_EWSR, 0x01, 0, 1, 0) + + # unprotect the flash + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_WREN, 0x01, 0, 1, 0) + + # erase sector + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_WREN, 0x20, 0, 3, address) + + if (not self._wait_write_done()): + return False + + return True + + def setup_chip_commands(self, jedec_id): + manufacturer_id = (jedec_id >> 16) & 0xFF + + if (manufacturer_id == 0xEF or manufacturer_id == 0xC8 or manufacturer_id == 0x1C): + self._i2c_write_reg(0x62, 0x06) # flash write enable op code + self._i2c_write_reg(0x63, 0x50) # flash write enable for volatile status register op code + self._i2c_write_reg(0x6A, 0x03) # flash read op code + self._i2c_write_reg(0x6B, 0x0B) # flash fast read op code + self._i2c_write_reg(0x6D, 0x02) # flash page program op code + self._i2c_write_reg(0x6E, 0x05) # flash read status low op code + + return True + + return False + + def read_flash(self, start, len): + crc = CRC() + + print "Reading " + str(len) + " bytes from address " + hex(start) + + data = self.spi_read(start, len) + + crc.process(data) + + local_crc = crc.get_crc() + dev_crc = self.spi_compute_crc(start, start + len - 1) + + if (local_crc != dev_crc): + print "CRC validation failed! Expected " + hex(local_crc) + ", received " + hex(dev_crc) + return None + + return data + + def program_flash(self, address, data): + length = len(data) + start_address = (address / self.SECTOR_SIZE) * self.SECTOR_SIZE + + if (address % self.SECTOR_SIZE != 0): + # start at a sector address + sector = self.read_flash(start_address, address - start_address) + + if (sector == None): + return False + + data = sector + data + + if (len(data) % self.SECTOR_SIZE != 0): + # end at a sector address + sector = self.read_flash(address + length, self.SECTOR_SIZE - (len(data) % self.SECTOR_SIZE)) + + if (sector == None): + return False + + data = data + sector + + for i in range(0, len(data), self.SEGMENT_SIZE): + address = start_address + i + + if (address % self.SECTOR_SIZE == 0): + print "Erasing sector at address " + hex(address) + + if (not self.erase_sector(address)): + print "Error erasing sector at address " + hex(address) + return False + + segment_data = data[i:i + 256] + + print "Writing " + str(len(segment_data)) + " bytes to address " + hex(address) + + if (not self.write_segment(address, segment_data)): + return False + + return True + + def write_segment(self, address, data): + crc = CRC() + length = len(data) + + # unprotect the status register + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_EWSR, 0x01, 0, 1, 0) + + # unprotect the flash + self.spi_common_command(self.SPI_CMD_WRITE_AFTER_WREN, 0x01, 0, 1, 0) + + if (self._should_program_page(data)): + # set program size - 1, 255 maximum + self._i2c_write_reg(0x71, (length - 1) & 0xFF) + + # set the programming address + self._i2c_write_reg(0x64, (address >> 16) & 0xFF) + self._i2c_write_reg(0x65, (address >> 8) & 0xFF) + self._i2c_write_reg(0x66, (address >> 0) & 0xFF) + + # write the content to register 0x70 + # we can only write 16 bytes at a time + for i in range(0, length, 16): + if (length - i >= 16): + self._i2c_write_bytes(0x70, data[i:i + 16]) + else: + self._i2c_write_bytes(0x70, data[i:i + length - i]) + + # start programming + self._i2c_write_reg(0x6F, 0xA0) + + crc.process(data) + + if (not self._wait_write_done()): + return False + + local_crc = crc.get_crc() + dev_crc = self.spi_compute_crc(address, address + length - 1) + + if (local_crc != dev_crc): + print "CRC validation failed! Expected " + hex(local_crc) + ", received " + hex(dev_crc) + return False + + return True diff --git a/RTD266xPy/rtd266x_flash.py b/RTD266xPy/rtd266x_flash.py new file mode 100644 index 0000000..27695a9 --- /dev/null +++ b/RTD266xPy/rtd266x_flash.py @@ -0,0 +1,119 @@ +import sys +from rtd266x.rtd266x import RTD266x +from argparse import ArgumentParser +from argparse import RawTextHelpFormatter + +def save_file(filename, data): + file = open(filename, "wb") + file.write(bytearray(data)) + file.close() + +def load_file(filename): + file = open(filename, "rb") + data = file.read() + file.close() + + return list(bytearray(data)) + +parser = ArgumentParser(description = "A tool to read and write the flash of an RTD266x display driver IC via I2C", epilog='''Examples: + +Read 1024 bytes to file out.bin using I2C interface 2: +rtd266x_flash -i 2 -r 1024 out.bin + +Write all bytes from file in.bin: +rtd266x_flash -w in.bin''', formatter_class = RawTextHelpFormatter) + +parser.add_argument("file", help = "File") +parser.add_argument("-i", "--interface", help = "Interface number (default = 2)", metavar = "x", type = int, default = 2) +parser.add_argument("-r", "--read", help = "Reads x bytes from the device to a file", metavar = "x", type = int) +parser.add_argument("-w", "--write", help = "Writes all bytes from a file to the device", action = "store_true") +parser.add_argument("-d", "--write_diff", help = "Writes all sectors from file which are different from file_base", metavar = "file_base") +parser.add_argument("-o", "--offset", help = "Read/write offset of x bytes", metavar = "x", type = int, default = 0) +parser.add_argument("-c", "--chip_erase", help = "Erases the entire chip", action = "store_true") +parser.add_argument("-s", "--no_reset", help = "Don't reset the device", action = "store_true") + +args = parser.parse_args() + +if (args.read == None and args.write_diff == None and args.write == False): + parser.print_help() + sys.exit(1) + +rtd = RTD266x(args.interface, 0x4A) + +if (not args.no_reset): + print "Resetting device..." + rtd.reset() + +print "Entering ISP mode..." + +if (not rtd.enter_isp_mode()): + print "Error: cannot enter ISP mode!" + sys.exit(2) + +print "Reading chip data..." + +jedec_id = rtd.read_jedec_id() +print "JEDEC ID: " + hex(jedec_id) + +if (not rtd.setup_chip_commands(jedec_id)): + print "Cannot setup chip commands! The flash chip id is unknown." + sys.exit(3) + +print "ID: " + hex(rtd.read_id()) +print "Status: " + hex(rtd.read_status()) + +print "Clearing lock bits..." +rtd.write_status(0) + +print "Status: " + hex(rtd.read_status()) + +if (args.chip_erase): + print "Erasing chip..." + rtd.erase_chip() + +if (args.read != None): + print "Reading " + str(args.read) + " bytes from offset " + hex(args.offset) + " to file " + args.file + "..." + + bytes = [] + block_size = 1024 + + for i in range(args.offset, args.offset + args.read, block_size): + if (i + block_size > args.offset + args.read): + read_len = args.offset + args.read - i + else: + read_len = block_size + + bytes += rtd.read_flash(i, read_len) + + if (bytes == None): + print "Error: cannot read flash!" + sys.exit(4) + + save_file(args.file, bytes) + +if (args.write): + print "Writing data from file " + args.file + " to offset " + str(args.offset) + "..." + data = load_file(args.file) + + if (not rtd.program_flash(args.offset, data)): + print "Error: cannot program flash!" + +if (args.write_diff != None): + print "Writing sectors from file " + args.file + " which differ from sectors of file " + args.write_diff + "..." + data = load_file(args.file) + data_base = load_file(args.write_diff) + + if (len(data) != len(data_base)): + print "Error: file lengths are different but must be equal!" + sys.exit(5) + + for i in range(0, len(data), rtd.SECTOR_SIZE): + sector = data[i:i + rtd.SECTOR_SIZE] + sector_base = data_base[i:i + rtd.SECTOR_SIZE] + + if (cmp(sector, sector_base) != 0): + rtd.program_flash(i, sector) + +if (not args.no_reset): + print "Resetting device..." + rtd.reset() \ No newline at end of file diff --git a/screenshot.png b/screenshot.png index 4d4d28d1d61bf9ad20ceb031385605afcc098da5..89cef99879beb2c45f73660ecce60f6a0b98daa4 100644 GIT binary patch literal 30339 zcmc$`2UJsA*ENiy5TwO&1*OJvQE7q-NH^dW3!tL(A|)Utgd(96l>oV-fCUg~RuE~@ z5;`IXNPwU;Nq|rcQ6RJk0Ybue0{Y1H@xK2%#`lf!XAB1^$G!L2Wvw;mT=hO98gDDn!$Mi!ic3dEdncq8c%KD8TdR@mR-y%=Kwhv zE)OyNct}3FYQS~Q4cS#8m>{rot6;W~-P4m7_ih_*=L@>0*8X{N;Yn|ME^4w{&CO7^ z&TknS*}7NnFTC7UXQVUavB<~Cw9fojMqODZHLt8PC(}((+ENQGeZjytIt*<_=i-0~ zPNd=CyG$LH>&Vhf4I@0B`bcUe7-c6fJQIcbCetFT^R1w>W~ENYQ+u%^KMU_SV_GP@6x5zae?J~1FrivIiiu3GO)?sz%z-@zRC2x&G$4nwDt@3o}M|4aAnDew->5DBc zTXdMc1k*}is?Z*z&ZFo!VK6Y$oY!^7dy(LKo*UObaf#mtesnec{)ww`pCCkg=|ikD z;m0>I`n#b`v3fr~if@O4zQ0%FL;q;hYuimIhRo_G)AoP=QP&GJv=@e2AXGn_wp$_e zv`8(-qPmvZIxHl8qKCZ&vg!MU&&B1?3!}`Mq(zyPzL1ere)=68s#nu?CF)1(hXZ#; zpOlG;oanGZRK@FL@AZr7n+ivRSH^l)EG4y<%NH9+H4fD?r&j7iM#z0fS0+T*Z)3p2 zf0t1zMTc``PJhjHOPX5>m-S1{sjRoDDVr?!&VJOK{DDywTvm~q;U}Kf_o~w@R@SZt zd$FM_eEhvS(Yp(0l{S(`t$i^Sv^LO@zZy&`z7)1(-HjU}FD4)^hhwni)tjQgk7`!P zs9<&akeHbcqMD5eYYt(pup#t092>hR6*f;kJDxnlllxVdX?oG^15Zg z_|qF>h1-rUJVCc#8i>Inpt#wiqNVINPR3P!%k(&He^RBga9#y7B^SjC>Ok(l@~O&r zzagdKONHd>klvK;atA@K+Se&<1VPl$3pLf_R;^QP8M1R)7_|n$UL?0Tt1mYwuE6Mb zG(#5XIsv`Qi^(Yb@?y=h5Pc9?=wSdVpqD*P|(#iJhW&f3nV~VIT ztG7_luES;jN;#ke>SH$VM zC_8p%4M_=ofVXgte6*eCy=9Ul^pa;$+>I*L*GA)ynmcGsy1i_2BZ<-cxIC+}ukA7o zDc~Z(&~+NL<=5TBc!k2Hks6WC|* zHEgM*l_d@Kt;Kvc5toemMnfHwXD;GbaMMyNQ!Cwli*_sNdqTd5L-$#0O`|$=#U3Am z#os8GGWdKnLeP*h?>+mcclOywJcK6{Xh~j>WZy1fp?F7R4u!XJGq zjE=~Mckh5-eiSwX=^P6{Z4R~Y0Z3oYyO{m zo%p1~W_zkV*=;n`ha>1Fp5iX@nQ7jay;n?aE6>4LXR3}&8}fb#EEBub*<5t^ZJt@b zOmvMDauGY5QH!j~!?tRgzc%&Vi3;sS=@hqP`(J2khPz)f|0b5U0ceJ`vLHzJ*QP7B%o(#2j<3{rbb9{B#M*A4{u@734fJ}~tA+c&4B(cj-*zpo2g+mC>MzXAT?G>kU>r-_tJ zzxM~Xz{Bo#%-ws|Uc(>up>;WaO<%wMz5(b4X6+?&7rb%n+RJzkWYhPH)AXUBwQKb? z|3-tqUmLKE4+37hc4pr;sPy-SPya6)9@b?ormqA7{h!F5vxBcSAatp$6xisZht_)8 zevfm9F%jKU=mP?W(Vrf6IoR*aHq7-5Ug?wDNgZWlEvv0uiwHp&jE&|zH9^xh$mP<1mm_=lExK7@)U^13SsgUL$}+;d_0q!-PtJDt=_L99cP0KPbGdH|OLK%S z?CrS?nD93R?MtcPh#Qe$s&k2Yei$BTjxZH0(lP(muA<3*%rtv+?g2t!>()A;L(^_H zE4O0uZH|gXQvvbwxD3fEg{*3oTXiiEmpfZ9;g3zDYg*XGylZ>HO1}`eT51#J6U)i$Fr_#73_g(Jin*Kl^*`g^hUf79p3YBdR7GtQ8)l@?X`jx0Pi2;^EJTwm}uA zv;z_wDc8xCHq`?OQrkWgaE#W#Sv6M9IVqPS%|6-pH666?I>+s6l} zrgc7rsYjcx?ymQT<8s>qCpK+!Z7OvA6t?^6CxS(;Av1;GSYJ)7`GX8NKOU>4HwPqc zf2uiIab4hwr`CZQc%FcBajhd=wIhBWUZtuV#D1o3`H;h!SpB2ILI`tm9m?pT>{l?q86ZS7c5pSphuMa`= zKjuj|y`TTs_;oPH17}^m86kF1u<9>xp0|CJkSW=n+`;nwyz-u74HXIE=vTI zT@8PoL5sxZm!yJ2wDmZnKM40582>os*OL`CJ`iTzMZ;Fc%E24=EljCK+YNj&+Nr&R zij!e47H>+YX3Xla7aKCZE#NE3ZDPc%0|;Pl8!!3Qz=zBrDsv+ z+}G}|uvW>f)KPmqOno@YvkbmK+i%LVR85>uCc3?%C!EDm`_zYYj1$Rz;$e+%ya z!)bANZnnD2jF{!=e*jW1?>lbVW94=2bE(iQqw7-G7Y4VkadB9qpfqyx*0)@GApQD| zaujH*S<>Hw>^J-O7?FWlQeJqc4K%(?fF|r9v+I@VBCxBt+unxGdnk8jHN%mS`#?Imt+Y#| zOL|2L;=@zflx3+=7T0U3u=Hn}g*Z+`LmUBkH{TL}(AEvR)XKuuSB`UW-R7+!xNZ>x z0shMha98yO$1}iBm*nA3>GF!eul%`Wz6)O@pp`-N49ng>Md%nceZbdwd&C?9{P6Yn zkLNTXTUvns*LxOd+wnffNja>33G}EIym~3em*@&`Oz`ULC<4wj#*pKgF*i8g3=EYX zp4iXn`ZUL%D{-98X22h%RE0t@MMXtdN|4~KOoLy4)5Cy)LC?hff3z;3fL^E~GqvbN zI?N&f%B?V4{ZS16%#}py#XR}gk$XKNQD3e=F|EyVJEPxHf|4H~THFebj^`GJ?1;kZ zVCWBi-cmRpq2C7-V- z)Vlzw@6Jgy`}xluRW#c0B^v70noB68?~UvyW4MPn|)yn3h6wLT-b zmVV!UPV`f+%dx9GTcJ&tmW@M(w|^C)K@&*F{c4n7^xF0{Z_b~Wca&m9dwF!ALQ&(m zF#PhDSoYN*jmTxYG%I9H_2irVz9k*f$ zs+)?D7by2(Mrk;l@*@@{-goGN0O$O#t2dDKw46QF(2mzbq?%vBT zq~@Hc5D0uOORrwx=d#;_A@z)~DMm)kuJ#Cja%fKonGu>!G;-SwohZ*&53aO)uj<>2 z^PbJM4*FnoPNyx=KjTQE$Ov(BagDZRe~+27cIi;~<>rXgd<3n}rMueO&@KwwbH39S z=aKrYs=)fssTY;n1qNJ(gNNqla1c%~lq3aP0odY)1*J6DV3X zfvAe9kx0K;^4$XiBk^;U>RoQ6R@~)ccoazq%!vQFdpnZ(E*ubPe$RD{ibLB-PFc1| z>|hS@raAnHzFL>*!&NeV47g>ipVuoP2pUWaRx=US5+gTe$D0kzYPZ$(ECt!7ZYRdO z!AX?<*?KIkI5!HTv7ciFL|3CpX((t>kZaLpcZD>*WX1Wq`@ErbsMoF~HK*g>{OrhN zHjSKz7Vai5x026|``1*`ZX?kq5C4r4I9^k|O>Ma;b{VtSbV~ak zo6H05x3y#RF)2$l(Iw7gF49!t!kJEF@SaP#siLMX*5XsFP)>gr{?Q*8H6w!h5W=RX ztCE*TRXZf;cYU%FDaCXAvL=6TuaXjPQMg&5gVnLMsJf0!Jll6XbeHh_QU-M(ZTuYH zHe%SrizvbET|KSA@a-?)6{!w3DGx#clUDz5-L^dpsb@~rAha1}gcoEq{z1qhoNMte zbaB;+aQYHPgdV=dxDE3rea zc6PRU6Mz0jq-tIKkZMvDZ; zlSr?53Z-)}@Sj)xFj1y)j1*Is)lF3PjN?kadyU8}fw!A-g=X5f%%*jW<#5=x!~Pr( zGV~efVi??|T~t3>F1oTQ$HNz<;?@uG(8*|Zv)>sVVUdS>@9S?D3!I2%`%Det@ zuM1UJ!IAt)h4=@47FM1bmdT5=*@mXr)AR4NCtv={op9#6;9WZy>S=L+XIgpMHm}t* zi?WD~mf-+_iY)2FaQAa16gl$oBJ*vp8RHY#e551iHa(_M7R$ z$VWzi`+3Lu$E}4w*czm|_so6v*5==XONrQ;@js0PIB~$WL?Xz9$NnZ~c{c|VT>VTr z=i;G;WzF`dx`2oON9|u4qMcIGcv2{}qy(EJriD&O zWX6+}yas<=zO|d42fr<&Jr=!EK^eFnR-uA-Sd3SGi7?N9?K+s>lWRI#vHeW2>-6KP zC^Y1vE&o<^kNlUuu^sH4n<1NotWOO1TBep*fo58)ZLJ@<h*fEtCw*O#+$wm9E>~^37*u#|4n7EnD-qIkQLIBneTAXvwdt56Qg#L z4>El4U-p=&GbcjO6YpaJ``X9`iV0|zKG!}57S5HPI-c8hh7&$1Z%qo%GZCg?-;W_{@@*|>40cd-nqW2DE-QcJ4n?7YKB&S|! z-N=WM$ZbJn8rx(bjleV4h&3n4Sar?3N!7QQ%!|Q_LLab%KRIov?c{7WuURluHs4a} zv1*T>#FwZ}SNzfVl9G-Rp{D49KhX%T{! z71-1ST36a$hjYI+@tR|dvR1?9Ut?E}AyeU4MrocOXQwC;IP#5vS^g0y0K3(D9$B>mKqpL=&!#4J`V)69$@glX$Bn%EuP#oY!LD2d%E^}f?K}@M`_jt;u2w?p=y~DhM%&oVF?93KJlsu4(krDh{G;*mQ9-HbrdAR@GnrBa2l>F zLXL!MyDU3J^z5N{d90fYU5I|i;CY36v(YWXR*xs%W~D&KYz=4+)2u5#&EhF#{UU)L zq}jv*lCgqEG;EgkKB9ner>krs0|AS@RXJ)W_d#Q4sxTzXDM|nh+84Hf<3AorG3@K(%8*(}r0eJFFrgqMBv-5a!z5Gn@f>VQQJ59!D><~=$TVH>OEe+8A@o{LfjVu; zZ<6}{iEZ?)Hf%JZFoX30+Y37`e*fpm6g3Ay%EB5P{{`u1f>mrY2$! zzotu3k4-KI0W0Q*?`K?g$fpgj#oeR3piy&|NWG0p6MLVo0}b55kitgxj?V5IPX!dZ z4wf5+zaKr}46Mt5ufNpc4;ngORZ)woId}3Cx9tw_i|@&p4KinX^q}Bk1L2cPYC+6M z@Dn#${cH6RD3f&jb|$e+JH}pW>#?7<>-8y+w_&bFQ9<3c%xAYI2lD{c;OqPD3?C~O zTz_%ZIX*e3vTLuKxw<;EVu@ufcve=Et6KZ{9@j0tEWblFUp)pqDJ2i)iW$=RGtU90 zReDduI>^qQ!gm7`VEK#T{vPZ8Z+VDcY?-3bKX=y-lj%=Yua@U4x~ zfQML5b^yRXfJwu7*JC5V$vrg~c{i$nF%TgC<03G0@I>Dq&~ty#0`&HwMI(RdABe+v zz1tx5BGuCownX2NG}+rDU??b1@!V;esPPl5qNAxsgJU+W@nKiYh04-WwNk8|TEx#C zB8`3%uEqcl*9q$g+ygt93++d@H#c5waU9Z|oU(3vg67GunkUvS&nDJbf8)=L`)S9} z-~cdH5*jjH%X?xQRNyQWQ*O8PF)`@ic!TE7wh?w4MVttBC@>da4F(=^fA@U6N(a!T zL09q=J(FY>J2t*pelO41lb_>@&(|q!KD#}w?@(e5rMUA_wTw^5wgVw^*><@HNgta} z0zl$4=z3M`^}NMgCSU$wleD%9T7VDS!r zrX2lFh0FU{m(fWvqeU_Q*52XBGOvWX!xa%=v(ehQ&wgcL{rqCY=6o?#M}xAUE0R0- zwUlr>k-He)-OdTBN8_hr#nU%!nW|#go@7*g?E#dYx_jWKlcKzYJ^wWXAog=@dd$mh z5jWUfWj~p=Uu46{FL6=ek~uo%7I+UT4EYymXz`t5*adl!lJdt%VAlhWThK4#Rng!- zQeaNl@cRnZTBT{N_pTn&;o?j{C3CVOg7_}WN)ec2d>Ev@A*Zq@5a46Rohen*qk6tT z9z{zO!Zbl6-jG|TS0>kbLMvb(ueH!e!OZ!Yd{1p)+Npu)YMV9)C)ChYD}()Kb7IvH3tiXg2tuTLsE~Vm<3Wtgr&4|x=tr`zE}2K zoPlCyHyR5H?YmOSmbSRaCo((zBx@PmG;{j#bbNluegj~YdUep|kLFZR)fW4wI`jn6 z$VY_{o zWmF8UG6{$EKzuveCLgKxDdUU;1qv+$RTVya?}YY+?p>_uXnS|3@;2BZ(-Qn)hc2d( z4`ThiE%}e3O<-sXcsSL6fRD4MCHJ8X(y81!yUg=d+6pV=2l!`Oi|BXgT_eGx10&hP zi&@`z!zLf+yH1N}2Mj!#`=}L_meOb$Tz&>*PY|QN?oL}Zg3{ugVCSJZ$i6N@`D$tD zUEb}5fHK4b(KqXteky+*94iGiK%QqOgm2<6k*?(zxy6|rF3#k%p9=Qu^NsM<)Cc%8 z#a4$h6{{?gX4a%?$k*0?!r*XZNxpXo-dQ@XfqSO1g;0W@4Z;gI)D=7rJR|AUq`8 zf%Q`~Z!xQ+{&}beAoXYS`!30n1o$BJavrZGLXPcw+3mWbpSk_$a~*@M%{xq?m{37} zNJcp4m=aaJ0nJnZ&(g!_Gf33X$-0^#kIDY9s{`loltwO^Fn5|M{Cv+z3)Rn|Hf4AA zcOB1Po$*SMW3Ya#?<%tlj^R+z--IBx)kwe_9XX1AGsCfw;lkPnIu(rigYJIcRB}9> zOo3(nMIQB)+A2}9X0$SWN@-BIT|*A?cB3gNRw-NJS%6$ITw?Wx*GU!SP?9v>R!lvdq779$I(=hMTqzM3s7vPRsWy<;Jt;9PY%A$Hi6 z;Zo#7h^THU)#_XnCo(t1u2U00kfR-3fJ<*5dwK@+!4UO^{Z=#dGw)VFej0u#v@i$gIzg0{` zkfNGG6M{MaH44ZNH93|m!(X3;G@vy!pb0ZB7t~`%&ui_r!}`d<0{v=`#ZNqpQE57pUTl%?Ah;iu=|N>5RW#H3 zOZ?f-73XMl-F(=HSsh8US$d*tYPyMwpwRm6S?1_T%S2utWMGyo z(SySX&7Ypn@1riwmpH25L4##L5UhKVhd6jN{ImuLo~mSzC-vO55}$Y+P+`RLtWEmz zSm6eg$>Do~4*itq4OoxH<1scQSIhk3a`}S5s)>iu7iry_zfH7;--6C4@FOv>Vasc zg7DCqme6Wrn?iWw65Dk=6|fV4s$m%3Uw;kkuA|d;!mC5QV(eA=DT)9(gTnO8%xd%N z@u?to#_TV?qm7@>_3dh|z>@rqix>T4lKaXQZ&~(^9)M!HZvf1vw$7zg>^UookW*Ws zl>u&&mz`xUjTc!dKN=s0P15zcT=B*6seeH++tJ`$?z)5xuo2ZE`SDF%uJiD0_tfz` zictS(o=W+8tlaVwOK=RU%Tt`=zG`nqOIa(l@~}VM>M#+}_+|IeQUYbC08Lf0*awR7 zbmK-*{C6MX4q_lN9!T^GiJyjZTp$$@NMqkIt5#3ygPTd8`(#t zQD0mlujDn7S(-!UtHk}P`m+(DAMOgiaBjqJ<%Iy>Mvp*~*MmBU?6VK7bdhk`6*%Ub z|DJo)<-@RK z4A+A7W;nX`<;T(XXKa8bA}gu6)OTII$QcsYkogQ=g!fT1{ij*fs042mJ_xD*3dM+a zyIc`(G=a->KA995R`_`x;DR4cAfQ)XuA0H8$I+n6?lO^GyF?n9rpuQ|e10#P5$jz7 z;Nb4FdXx^Euh~@VD2(XYpXQqrZj=R{%JsMpc6jt#I}V)czA`<~jile2U7kOng8_gK zG27KN4E+6#3juKNR>xl*3&$n=Un=vzAX0#=-MG~oP z;?@}8QNj`}A}ZYy5fR}vYsdNtkhGIIxqaWW&`xu*Hwprq0lru2nNn=TS}?M0H$d~a z^V0ElY@ZpT}ZNMJS5||2Oy?KF>)Nd?WrNiJ*Qb6-cFB7>YW9+_fNKwT4kS zEpefL=uH^;ZP06~^Me+je+{_S1P^)y8{kd(i@dDi#gy`|LJ0#Q0i?d_)WE5kc*`*6 zydHftW;WDg_riOXVxI!WU?8AqG?!T?-N9P(QUfIe6_=2|#8!J8qG^!&k2$zSdh6KX zaB6nr=u9$p*^oXxuSH)tBWah^$e3^hxD-|=PZ(wJqSSJPaN2f;CZd|I89NZHBof{)_%TNlG zMi8iPuy(aIV13L5UNS@%UXV4Zj83!3+{6t}OiV-S`Yu&x0ze|84A%=A%(NJYyjy0C zC{kZ-oP@AUW$gb2l8Rmhd3%~>`}&6I)PVoE&!0tSGQ{d`dL*E&L&}CCeCN+LiwmpD#s$ zPcaQa8eGO~R9MZ!rxg|y*t@|#l(p1jglQNkE*{2sf96elXF}-2n5Ukk1{|+y*fu{0 zccPp$hm7xNq#`=VQg3QskkNn4_8! z)68&RrdoQz`DDLAp$T|W%Yg%}Z*OKGw|88xRUp4U2WQ!k+Phs}b;QVG;&fJ4Jsi zBZ*_huw^RTFR9B>uU_vAKtzcx{B0Pn{cpDbaDtm4`e7P%i?7at&nwvFjjx!7oBpX% zEDO=6Bt&PxD+tNhyVe5724e2!-rp=AX!9bp;-w>Q6ehcWeN2?F`n&SsyK{kru{DzYPC52#}*6F%5J8yzJ<$=Pxu)+3OWAfVr8 z_isRoM-*U5|7Ya%Jt<2q=chr28hCjbY=&Dk{(}$%TuBt*x_-kvYVQ0Uzyi>izQ8(Z zpMqFWUg5~W?t^16yD{qH z#XcAb$;d*c9U4ND@|c?LnRs_IEym^X#?DX_F)>?mZ^J(_6~Dt;2M{2hT|cHIf%%iW z?UKJVskkjqon-2KoLkpw05z%I<|6qrJeXEymMLN8o%U$sJ?#8;jnwQykFRZ&?Zj+N zL^i(#7Ll55I#AM|hqnh5=O6O#;i4ZXI3|%$Oj$Yr*HJdMyXseMlPKtVB&|*m60;Z- zMVz-h)BhsSR8|V0`~hTT$?Gp4{<6{`r z7lz;YUS+v=r#ubUyT6h$%KB*irC=AK0O@$r4mG*?o4!hL1b z3ZB|UdqC2`j_^YYXYu&YAzDFhw#AhVOUM0elZQix#})5S;Z69e3uk>aCd0ZDqg8|V z0+hf}K<=x%IXTUJ6&B0zGh}|r z8>iWgys_%*3e!uDSMN0g_W6cTZ-La9{;}e5K(hYuLSibAx zOPvhL^!o(Sm)!KcoLaJ5 zo_@po97C!;4T&O2e5@W^{z-GZ6>x!m+TdCz{h?riM_mF$^zncFX$Poulw8<@rO1=q z#U_2>8kydoAZY)znVMwKKl=(W5@Q46p;<|#^l?3jl$l5dkqv+mkEZ*qe;xJnBRQ&+yC8DT2wL@(Q z{B&m71zU}LMN|$;Vpe`Fm|y<34SKW(vZrC4)_Oz;vdvEOSnRD@*v>PZ8DH>RCCbMJ z@}}hNvN|46;8i%OJEUpe$F^R8&+Kn=P0tY_#bgfHX!+Ujj((##@1*-wf92M9!o zL2qBs$-w|A&%b5v#4iF^^S={7CgoTC#4nx%;Bdd2^b>%GtNWb+`<0jb4?z6k!G6&} zfh@vJdmQAV01IrpGW@(VW@d*QQ`x!1Q4>oHyKO-eeK(PQLObUsHrMgj{A)cvNb#)F z?jNMZnBPS(f7>dvzks-p#7k_>+ZYI`I&`rWWJ2af$j zY$;U$V+;c`hN7f+fMD3)HjOtc7&6uGcODQ*vZy(gz0rnG75V0T5sn!(sQ>wCw{w(t1U1HB4bh zL-dz4r54`52`ceU=!Ywr-$SIOB2w}t$wwHHc7G6zU+7Hx@`f6Q*G$*afo$-}^!KAs5atZE5@#R`P zVI?=xRKc3@>^J$*wc#$i&A1KSimR((W1#3 z9kLVp=|*~=sn=sE?r%InB{v5DesIEC3f4amPHiZh^pSkW8i@T>m-v1*zHEc;D<^AD z`AV_79DSz%5bkWLpu=+&B|2T~7GAE_7y-zW`)g^oBWYZk-F&H`-8MiVdoSrvk&pu9I+pG;mG8**hupkaA+^=I+L31JRS$ zKmAVD*;K=K10WeW)Va04fgbN<>K?sP@f$L8Il_`k2 ze*;IlprK&zXFeCak&HXfm*4i8rzBM%4&bLK1w^gf4^WIt;@6YU=Kw1D^x_fUz?mk^ z&?j;=iOgaWpl!jPPkz)SmG!OKr`P|k_MqK&>-HTMDN%=FQ~*xdmCK%YG_P^@^QjKc zM868GF~}QG;>O&#bqZYDWqzyVfFN7Qhy4kUHg3?7i=!?^G~S7>JlCzL_*Wsote2~L zrX;=VwW0s@!Pz1cYsD|qTwYY+Uo|%zv;-cG2wZyUDm^LukrJqFH9Tu!`{y|G$bn`* zMMbHk=u^h6(N9|ojo`SQTNX&ydRoU6(#!CqS*N5-%0!IMu8Qgo&!_!UBF6VwhWO(1 z&L}ZG^z(RiCYty=$(PI3*BTz}z%_4=BsTb}> z@WKuZDVo7?=0?siHRecnl{Z7KY%s|iG&7tPc!{4cXY%@rJRR`kqsceEebD})AT#I$X$pY;f+LMQ?sPjM`vC0 z3hYj@i$Mp66~dP)?k zWrQJa+%p;Fk(5J6MAYSKe9LU$2E7r1-@aU^ljE;Am#9)QPm|x(7xTdkimCf)=OO4# zyiu1pEZ)!P7TBT>2S(2)33}U4cC-C%PvE57aApP|dz93k)U6+ONSB(9KSDH4V=Y{D zsxyTlvnTvvD)mR`Mer4x{ANi7KrP>Lb zEfiBtp;SbH<5iaZ`rxL-Sj25GR1Ot)p)*Vea>29zeByh&f})1NbcuTA9iqA?lPzqb z(yx72{yzJFb8hPxwu78vknnyZKgtT3S_h2f5kPt>txJ0aLvOb)-M&-Wumv)GbQ_f8 zrlx;}WE&y%203l*qJYE?Xvu)t`LEwM{lEA7|3(aRBoy1SEK}_Jcba>IK4||o-Sq7h zXHGU1P{6m9la(rY@h5Zr-39-qW(%lM0aU!5&O@cdt9ZdTG@_iDSdmSBOOPzAa08b&5~`b${u8fo)U0JCz=Nkm0=bhZH=l*7-qq*U~sar~R5< zycHG;acu&x!~4H^A2(0*i>|HOuJWAJq{rjGE7S_^%(Un2dol31@?(tH%}-08SK=Mp z@P}yE1R?imB(8VKmp@+9M&_Dz^((Ff8faixKRJiB6k&Tb7&$#DaHXWYer&L6+;OT5 z{l{0XN=c7STvtYoV9mMg9wpcSZ64!AI?S-S_K4i8r&eNplfjs0NnW?MvOVn1t z5x>&VWsF-V^HdwgvZ#RzuM6za*btEtKjJ*3K-sD;i%`d#^x_Gx{9P#K8j5};V`t35 zIJOHSY4ppkkT3;E&Rx%4p6F8;+uX*F3g1jd=rp-V=6uG9c#sT9ZYE+*s#C`Eef^PE z>6`cB<9FU_8p(s`_f{j_qrun#V@^+R5^kVuP~rRxBZ$9K>IcI z@c(aS3&=W1DMVDw?ToRe`TeqcR)15*M9e@r+gK0EDKPk+zA*S;wYsOyl=Gm4>yRh^ zsmfuZ!7eT?wQe+NNgzdF=FqCp61Vz@0D$9?U9C{sJue7>0?7ve;d|Q*h_JZ0BGKOy zYx1hT0m3P#C`kyYwMr>OgGVl}>w;sE@~fV6k~?l-^sWKLQO+<9#9s|SbdJfx=C0|Aqa`HnYk9; zWi-D9(zldrq1fMkVr+lPbgY(L-bW$8-PY-WaO`HyYHFlVAm{?W=qv>U5qh<*EkOM1 zi@AX@Q3?Y=xF<6mguUobzpQ|ELk%#T?Mgu)2zf%W$2q&J_ePck8r$~pHH9|r zVM=*}3&QJ}CJH_x-Q3zUcD=k)?135a{oDH<8d&Yc8ebum?8=;vMm^v?yX94 z+x|52U+L;(z2YMGvW&U6{ir|DJD~>CafvosDvXb$W6MYoWPbg=T0Mo&q5$>1Eul7x z2@iyoCHUPOD}R|=^vsZN*Dc-JEXA;Kdkg*pg^m9-ZDo^|d9GDn3?axqrvn=|XKD*e zedayLy1pm)9H<~4Fv3865a0S*h$ckz%a+Jb5B=1S)xf;Qj5>^6^gmIEJjK0o=;q>; zjB+Ela2W0!NyJ}_T`k}0)3qZ9u=dUKEPDAk@^;4DtMVhqx4BcR&uUB-z|tm>x8uOj zG%_-5r8YNl*EV2vqvo*!H4;GT+aDbH^oqfMO6~@L)CLFjEAa@DNqo}A5^C>tRc2%u zC0;f6zWJgv8PLebkI~;K`pzrif}*2vVakIFJi{f(@~PP6b{JL*PM)YP^{eQnp9_zw zl%VwayPksbT4V%O1}0Ai0fFMH8WCVM+Y=1Wq2ey`ANZ^UsjptQj_YdjGRhs%U?q>0 z2Gnh5h_3tf$3%ZEvR;X=S~g@;%;|Lo0VomD1#+U`5dD{y8cI2BrJ~)}1A*W@gb&h? zz3vPFMro@RP|$TsJp31f0W2&>xgGqpA1(UJYUq0a0z;z$q(0!6iE@ZWgVm6~>tY}J zO@X@}NL1$M_FPq&fucC^3T4bY@9e6)e>j8HKDI^yIa%wfdDcqkA^)J^_3L+XSeJE` zmiE9?Kz=I902iE|%(Tk2COP-`kes7J$vKDSs0^G@Xf@kyoji7uFAkTu9INugaI;k@^PO~XMb&l#B-NdMf@KrX11hBW#$9C?|$@wW=g@tvya_C zt(aoM`)<43w(Q2&tB-<#?;Sn}B&uJnaf+=2f(l^D|1%qa@l!-i90i`j5IneC-8(v$ z9AEYBiT!!IT?;OF5$>gH&d?5Z80JEo{#DtY`2rYO);`>Q=i>*sz^0}5E3%C#iCJs+ z%*l-4EHdDX*6NH;yFgc!!W02)>J1cyv9+?iepprHq9uO|mPQ9ByL{#h=!PSXR`fT=QLQ~%Cq)y;h0iVz zhx;d7x1%1|ps(cn#-_op+Mf07fq$Pr1$p}#CkwZCN0-I6#UM-okTyFCB*Mk4uk8n( zo{=N)vFJwVoT}8e{pi<4LWgkH0eIX%j5pc`F|XGlY?9D8lBeiL9SwOAA-h`1^=l(Q zYKS)S6%gyNNn7M`Cl++HzbcTI>K-gAmsuQcHy!9S!dH{1CG)$x$xXr+-8O9Igy)z= zz9$=T5%sI5j#vd^d3EDd&kn9eFabimgSJlfAIQT&G}w zf%i&4V9P^Faybc!>l<6Mq-bzSEsLN2?e2pqG3d2}2<2;wtJAoy*VPM+rJ$P1iHWhV zingcO{7Fa;f?OArEB1PuRZ)4ZyXeHtiphwwkc0^FM8fRceVUCXz~o=0y}(z%<6~hp zMRO_C_p?57$ej_X6bJ$Dc#U-C0WA~XJH5ZduDUNxKHKwI{@PLgp|AiNa=_R4WI)*E zbgvJXAydL0H@|m5o?>0|A4yuXwH2BQyvyBVvvb(xIB66c z0QnCzjl!e9d8^>-`fYa?Y97K_2p-7!n2`z@Mkrdz@SoaO>cu!>&9MkSO~I8v>jkOZu_u)Pn5>&ms7tNLnW3 zR;u8W64!JROgY*@LW^U7)?*6Hu0tY&T#bJXxDc6yL*Z{svT0GGvgp@C;Vq(9%UMs< zLBhH8L_F`xNzp6#TpJQll_f=b4LKH4$kZIxggLD)5Vnq$7+|KC2k~>XJQgPPhTGVd7mB8}lypblq z*gyhG9k6@rEeiwj4lhR7_|NvtH5B+^mgDcdo~;dX=`h{*+s1N z{KqZFgNHkvaYv34Zaz%>=RP|85WHs-6vKMJ|9^VB@^C2Ezpr$d4l&dr5k?CV$CfqQ z&|YXGdlZr^k+F}Ki7A!(p_CYvsD!Lzr;tLXl65eJF?PZjj4|*1%v45o-gCXz^}F8r zr|Z(pb3ga;e7Dc{^Le}>20P|r34z66hwz>M^)6N-lv94XR43diM>Vqgp?6`|U|mxO zknD16wnRqgR8_I5OpRFb zIWr;TRJ!i9B|lC87Om%K@ByJ3j=tp0YmvJ@C-HaVL8InNtE~2! zAwRboyOig3?LNd0?e%x?h^zGz{cE1|*Pdt5kRM2osiik;xn!uhQ7hl$QbQRFFjy@8 zD3`(?3TqU&x7=UHQw3Oat`8a}@v;$H$)QHZ<_J#RL!vszG0uy^sHgjkUu^W33p z@*@ONrPf|^5WBf@@r*-_d>l20%L{4<`@kK5Hm3+>q!cZ%NPxWlyX69jm8B#7aR%8`@2!h6LAV20txeFXv-<>D zRS$dp_;1U`7N-N47|;m{(Li*^EEy4WrOci|Rsk0V(%YKF^md{RgFVctq$h*X($(d& zaOV}u0_^4`141nz-zA2mf4%;X4g4mP_W=VBo39BwgZ|8mu9$!AHNO-8505|K#(MUc z)5EbDprItb3G{w@-knbek^!TnXCa#tNSZ_^v9ztls`1@f_%2iDgozx}`sv5+g`M_| z6F0gjgR`MD-)#R*)!b<$~j!!jktx}*+wA6n}Dc7mF`yx z7}-%;ux;ou`mC*`QZl%EN^h4M;-vQ|x@;o>d=xI76T@2aj@+Bj>;l}P=7D30JbV*@ zZATLI2p2JfltG3s&G%^Y9q(6mudS433gB751ApH!$8iKk$`CdJs>1@chM}MXvb+13 z((O*?Vc%e(guCw&-wZXAHP4grmk=Wbb*KD13yCRd`K2j-wMFEh5g;U!5gY*rOjdAg4znob;Vp(9YM#&Sj{e5Z36LhinF=2TFifp0<`0eV#;^<8;)r1CURkHa zD}wPFYw&uQ+#c&pUoYIaSGrt!MoY;J|B_e+(u4%kzJUrx$9L>q<2J}ho`M9#!qo5S{A`1 z=j}yUd1*K`ny^zZ$*{_J5kZG~!q^12?asgzkh=prxMv23SD8%d9Rby^fGt1!8USa% z%V$Xz^=LJ~AL^{e0BJb@q%Kj8K#sw?8{S!hPP4+eT~#0nU8={%FvjXe6tCwNQUPJg z@3bkL@E@(D8;nL620-EMhp$U5hosr|?15$3@N84&McJ(>U6>n^wIm91WRMJF7<%<` zAz2wjm8aOJ3JHMDmN2G?vaBA3@bGUHK!MrJ6D)SN6Q2L{aRHX5Yc8t`o8_ar4k{3P zog|?YmwgqM((9@3L^1zS2y{^2h-p>4v@VVtvOQcQixm(wE2mb-IXEQnAh#Uow^`7( zq{TScR5RXc#2!EEl4xBK9S1&HEdQRfq`q`>{&Ou~8{xFE^9AB_YAQ!TkLY z=$Kojsr-UKX0~NFbMd{YkMfxf-2%DhO$rjdm?C^*GwoQOz`GOVN0}tHjmlFasO_H3D&T$FL|X_0G6G=T*!<> zof|Ms9+JzeDCrz3FbRrhZ}RVIUWDKhJ@2;EIW74VQTvwa^4|+HBFAxog}vRwI)|NvK-y zJCWlW^bAUS^J_%AL83G4YgLD}t)^fGL^Adcb$lIT{3dI6(s)GZ> zXt@(vd2JLd0y9__4a-tbYx+nJsq-_STcqvPZ3+u-v`Vd6|4l{7UDRAg@r+r_2MiMHE~6eqgdh)0}a28bXq^%uPl zMWpezyrXWtwe}C6t>2j3)yLm`NEZb~CnK65u7_DlAt>yD`?f|MO*G<6W%H2q7yNO@lGZrbpcyk8~-CMu>G9 zgk+biFmUC$)TS0A7kI1l$MUT8nS$WK3naagXr&f@Qp9N8|7qrO_!F%!1E_b1M5V6B z4GDkR32s6;Ocew8MX7DgK=$=MoiJisc3zk;foPF(XeaETbdAh~SGr>*t54$KPA%>Z zVR|_e`k8-yzk#f7+tf@m>RA5l*a3QRT5t#4Y281p324!pe7Vzkr;&F5z5~3{v-reV z=?XhjE5f#d>+f}lC>L$Ry@{4Q>DFxvK!9Ki{>0RWL|$5ndhrmo$=)n1Vbter+y_Qw zDq-j_PrRpL>&C^Ia-6oJDDJ&cYmwNt3fuMldjgVp)_7c6L$LRlN>7Vlb?q6c{Ghv_K-4>~KWQ=`s@ym=z) z0uuWLVN&5v$~8R?EW|nHxEr1B8MOYSm}iYva*3Ct!0?={3{)*+3#xV2EPK3b&T+5n z-aIv!*;uhzZMIQ3zud4YRVrdDulz!gbD*#Il~MG$j5M#}pzSEGUe6Xu+#ysy+^LH7 zJVAX}Vv@*>sL&AwZtIb4or>)Jn=e-xE1Z~nLn>zwqnMR3jW=nE^%v}L`+kvwVx8*1 z%_~NSJ|VG!`ZGz&;-@V9hN|rW`IFqO{D=@i$O2|Py~G~tYaYxL1n|&zC8?Y75WdWe<7neakIQM+I(RxZ}4^%(9X1-RI&eoeyyW>3rytu;Q%LyCKv zfNQOlTJLh2A`2QCzcwwPDbIkLN@Fm9hW~sOM^A0&T%$VXGp^sH>gjHnkb_u=*|L#V zq=XM_S1__<6A}^JW!6jM5j-P1QtgMJ>&Ro zak#kHcIq4aj~!!gs5H=kILj*9v#mA@Nt_lyXei_y+v;=2r8E{Wd;f|u9{yo1L8Fu` zzIHNbXpg;?K<}}Q<}-tq<<^I9>{1Y=2_kOL=Tx7(>qmb$H(#!~co6x4GVAl zAv7p!vCyx4Sl2*hSPJ>PKIM=Sca+Zyz`Kdg2528cwRo6K$5o2X&vkx&z@rs4QPfl` zKBL}LNVJdl@Mt?ww$M_KO4u5G`%aCuLbMp7K__%85_b2GFUbOyf*J8cRx&DoLX!)i zb^7z=_#SOa7X?tAokK7!&uxpB<%0c#U9c=A*L~ZVoi3S`E=QH37p{*4ghDX~TQ%%k zU_h1;QOlcLZKEWh#&XE^2$P5FuVrd@2I|k4moJOe(TyYUAkcDI^!UU0>w^`WiuIDu zJSuD;q4Lk5B!mMB>fDSXEm0Nlhax%w531hZI&sVJi-N=K-kU&S*Df=ZL9MT)mso^= z7uzs9Y8PH#^-U<#Zz?DDm1~ZSWpEVrfjfpFcrr@kq;%V}F7Jk^oMGJ$Xg+e$Z+!yz zIlQ4y2e>ven{$L$6x7KN74qPPtX+u>TX}cCovckdsz8Je6iZd$3!*Bc#WGlr;a5~g zvDir?qyuXycBW{VBZrnzznXwFW^KXYKM}pJN}{H>7f_t#AWKex0x!{jG1MWR2YON0 z^xG#tC*E-}ws-Kjt32CL+rLoQM4|4fuiPoVHG)P^8V~NJMXh{7BT18{FC=nm{X&<%Yz^+cncyOXF;S=f$@#l*QZg zT7?r;0EXAf5n2M<@qiIO=MnBukF(&|Yf$!Yh&k;FPwet{*#GqQvdeZU3q3c<($x-` zTfICteCUj7@S}QIM%4(M?-*(CIb16shJ-a3e9%s@f?t$~Wbpr))k3joX(>}p0wmGS ziNe?q4BtAY_++9&X4E``SRRUw69!2ARaTrX#w&rqjOz}aDCoI(99bkfY3A7$d4I6F z#HLKH%x_J0nDE~UQS?>G5|OrX?#z_3VQ{Y?2$rCgzN~h9Jt~N6WLyW3`FIj|#)UT2 z$l#3Hx29{*m>oJ9;DAjPp3=Du#T9HBx&B5Rkgw?Ic3K#`5NMvvw@_Z1$;&Tn%1@5p zr7cpZOk?UXRJVU25vA0M4MJduzTQtrm_O<^{jRg$5I}IwzNb7K0L=#9YugXZ!h&AG zk+qYN`L#zrpKKFgPEY%kHW2;V1xZIfUK(BmnlQP}!fU z7)rM;ETTGcOODOcgN!K;&7Wo%W(1 zk_PHYBJ5}c9t`+g7D9!@EliyR;`il`>;!bOrFyoE`KPZ!UPUoHa&y(H;mFY}rR6-d z+kCEl#&c;?`1DFYiA7hj5hb<3g40-QZAvo|{UQ{Sz^<4Et!VZZehw z{n`i@nF2V)AVy~~jV|jm23A6P?s)g(A5%?(_3b+&17|g-JHvb(j9-PHy9MNFa-K8i zK{WB^a-h^mpf_P(4q{9kJdVIawlcmnQvCMpcikWJzyt)z-Ov-rx0}ch&P)1l&r6~l z<`msOH=bv6mrkr3)nh1-km>`Jt6x_(z+W<6?xmd-G39sMZ1Qa+kFod^n&aVFPxfG| z$MN=7KpFXUKX6@a+wkBGus^@f=@x);PUo%STF301OSy0be}l$-X+Up&PoRdko13c) z05bJ*go{6`zri6U%{^a^c}U`I)(%*eonQbu$Ry;AnGmtL4%2Wk3q zgnSJ-2Eg|qBGLGDx7>bIfXm{{coBeYwZ-o+YOm;|u-qwT3OYXIk(R(~9~IDyyN*2e z7t0~c23PO@B?#D14?_3pT?49}UI!_UY^zL{X^X5}(-F3>Jj7!kOUOpz8rdoFCop-l zDDv|Z62>Go`g6m|O@9+Kfd?1lC6#*& zeD%50An5QK#6!s?gzave;gH@59`>8&{E+GH(6IN5%&w`>*edNDOkpC?wk!LlevCQhym$^}01{MZ? zzUSai;0>T~lu(;(w2*~yHCe@Gz(XAPJWlrITZ%<_dqMvVgk20~J6>_%h)|`euEqWk zR9Qja^HGTcfUXmIqVV!kV7CBoTi*jP_BdRK=NAsF9W2?XqUa>lGSMfCvw{Bo9-2V> zy}Ih{0QknvzNLt80^o4u3OXdt8^>2u=L$L#V)CW#rv9B<^raSjzf(Q}PEcYH%HhByH7Eb)B63ArS!8?R41S!3s zlahL&2V+VDtV1)vCTeZuyg#AmVF)EcC+MM}aNl;vhZ^D}GV_XSj zaTEV_zS`8b(ii@*)VlY`+kY$dH(LD#j!-JP^fcsorwIP^q=jjQn&CYqGT(>{h%@>6eif_I7Wuik4*x+)$Tj9c} zpzP9uJAMuLXVgR)n0V`YRA~E?-)%!hDcMP~ zW>f+Rd;ZIMe(w5zI`RBlfcVujsIBHK4#31|)AwXRWkyy9x&-HUAx8vM zD?pn19g%{%w}gbwA{VC*VTC3pRVz8}2l7BdLb5Dym_6vy3(f0OW+y3m+4(+8FE>t? zSSdbVVWf})3D$$p_thd%S}R_L3w(PK4CnakH3LrZNt-Oa&ilFbS^KzHs`nfh0dErW z{{zmFTgPIx5dX)`f}F05jOlLZ_$^|rOD_cN9C)3=QI4%pKKX4il5IN>GBN^I>CDp* za?bwz!yC}9f_^fyJOTtxvp!$tfX{`F2{wAIfj8ICGtA9qU+YUGJaDh>;LpC&a_UBask)B!`Fh7Y!cX zDcrf!z?J0FD^84>Y9-i*9#G{7_cAB`M zXUX?fTt+r_0+h{l=eT`d5(#Zqg>h$vItmyP?CRbwpLl#JmL~iD9g~+3iLWN^Pae6m z7l$f~n?4ez;C;yT{>&2}RH%XQh`-*3T@?7qgJ$d9n>Uh7>g@a;V#57tet5MZf(}$? zwT$=DjBx!2<+jgpsri-Eq|S;Ns>Zm~a>jP)<_~wWE!UKbzhoP62_C?pGb`dbzH-?z&elb5(4_#q6BXBR+g=>>FZfU}Upqap{KZY-#2G zSgF=tI9c?;z|rlm*tXl0d4%>wzC!!TWW?CAO-bf@kn6$J!Bc5+p@X5U{W;r%dabQ_ zp-%aI7P!Y)$8Y+W|8qi(jql+dyZ8OfVai?{K_Q940SdpTCst!vmw@ zTVF?=jVaqfO%pmY)>^i+Ig^LG-Uh~0@YqPm#9KN_^^M+pSDdXg`lf*b-<~=c_CslZ zXJKz%$#t?cX18l$xB6g??_k4q73q7>d9b)%NwdcrTiaO`8`WaH@ky}2ODBqScYMg! zS{Te4MvoE@vr7z#zHmOz&{KkK7p@WfwrRCWJT&d83m}?e=;BM!_UQ5<~%EE3#%x25M zmUHErt!s?aZj;ugMiQH7)@`cJTK8&MQ5q5a?>>tv3~TqALF;deOS-kY+LltH2RV_& zJQb)`{n|#?6*sGf^teQa_ynu)MZZK^pWUZB4Giwj%)|bo;z1+<0`;mP zpbvIz+l@vYj3M9eUD(=o-RL>Ms=2Ob9r%y1cnKXS?PpXjk-Ki7_sB<9a)tIwZ)+S5 zsfUhDG)c)XRFd~Pg(tL+lqIR!Apxw4iC$DONIFo}c|q4RqB7ss_3d$x2cNwJPX;{K zsjuXnQ~M}y5z{b)akjq;l9R*~`n}lj<%>j1gOYtWx~LoJ=YXLV$_-f~NL7cw!!qo_ z-L;Hx!~xcKzruBSYJYCzfQ;&3oO<7OViAOF?FcR_-c?MzLW=LD8enT;DT6<$FXuQI zc>OM^ReX=&hvaoVn<4I|;hIICM-?yG1YJTf+CEGRnj7&@XC0Kv3GImOBM-KB_P@HWc~xw%xGYcyRab5=A3ULU-EuuhI+!@v zK8Uw6zXc0B&42w$#hwT_b;8j#!0&Z&wk*zE)<``Xf-msdDuReXDMeC@irULHY%0p~ zls?BwSc;lia|v)L=M6bZ-%bYG=11}5;_3u~-Xf1>0#zG7!V6>jhF7RPR47~*w~VDc@c{H#)~ z<8t^bwQ@*%8#RrE+V#D*avflby>D@P#PydjkDlRHE%k{9{0^vG=BanrK&<P}7aALLlWP~G6!M58N5&?flOZ$;odNN%q<{aOE76nHK%g>r{GnHJo^XxWd#DzS1 z*_)4KZI!kTo<65yy#Hb+6YDU-o%^X{j8%krSt!^C@4D+51LVT-h|!{1?-|4%gKPwi*fj#y$jQk|r~ zF5LRRZ|gt28imU`{k6Q^`PC(0Y;L*rwtXX~+;&6&9j;9ebV$-l+599iJqkia>PscS z%Rl{Wb-OQ6%r~0fF~ciRuf+1U+j-0K%L%b*->=FxalslMeKI+IZSWRs)##&gZs~EJl zd7#juH=QSv)lgB?%`=lk;NB#I;qR7 z$OS-h;T$i{G%gz;%%9z{j#PxlqKN2V8RTNv;$HvoEt~u=GCgVz@r?Q73iRaGSW^#E zX>SzJ7hZX-mu#qQyJFFU<%RL1#KC*8 z?;l3%b)JG1)S&HFAy^H%R~yV|P*26>6)aSyuCQ z=DeXoH+v`vQ&Ttul^=p@E5Jh695SSHgLQdE))c$J!6C8i_dOGIIPDgf8ltU5ZMFIG z1T_0rl;eWkjgst7&)J*!Qm3K#T`1Th^m*spS1I{mnNRzo8<@(Ywk=)0+kpq-Ogi%G zj9>2ohuF`}f7(1oy7)QjTyr}E$Af#9Z^>VMdyD3a>lB}Dh?XnfeCJg#romP>T6Ze3 zZ}KTgH?=HJHAa~tXMXi2Ljl}khV8e2UydcMZC6y#D z8dfq^{ly=cV1Zf0{8yV7j%8n_azq&kx@1=V>Rsul66y{^wB0Fbi&IIgW+(7Lg3BM3 zM>nLu+RoVQxsg}>Fa*s1K|^^U(b&J|^iC|FsudR)|srf)Q z*jruWrzDc^lPNW5I5WrJsD>M)p^frGWg1zXiTP+6U^3IP6k4&y$hMp4@=~g_c8;>i z@9Xwd&P=}NeeLxW3oStH0w{5Ao|20Ft9Q$zm(s1a>x^pOj_why@Zac)9<5d^!QXHj zYNNd0u6;4iqd*iQlIRC<8yb*9i3@Su)cHYW&*tf8krvpH`84RUYNa=0@z+&+gR2^~ z>(k;m08rXmBPCNpsrT$SD8{?0cl)oFbkn=9LM_Bp^s~RL088?5m?JdqCJFD}^wpvG z-E>6*YuNDi;wJCp=$ZveNx68kKOMTa>Fynt-?3Yk%?z!`GWRWyV$&p=L(IQuyQx4- zdO2Jeg=b?X1wK=x1=mC9hb?FI;JYzmeXe<5J_|nc09{X7hdJ zde45p*}=M55>a#fRFdm<(nPvXwY6RJLdP-~v$m3ixg^xRhhFo#F=Pl1=e3Zv4m+1} zXT7d?Fauf5-O&gd0+sfz8kPHhl{;QQ722ceZMRYHwOPG?Zm~YivNC?_VcK6jREpGs z(3~aSYC&}6s#N7_wa{*L%ywtecxAf`vNmRFndxI=^oaj@%py*iAErPny8T5$i#e8O zpI(fIi|TH3o*wa?5(h5$uARMIzO_~UlDIYxV0<&$k8HL-WWQZ&TZ-CcQ>WxR1JBO- zfZ6N|#!e#3V0@esc*}l&z;(w=D&a*!nE9%`=6HQ6`kiWF-+~0r?g4WofZoT zyo^MCeRFayyWv7B@QSTwvqY)%?d_h*{k@TcJ>GsoaGaLmD$%=Ynq5^-ipJL|l4D`> zwrnL^1$C{X)%mu}LTF%iLKK0IX$0iamJ9Xgm(4c_6=LKdc6Y0@7eDhm_wQ9myJ|4O zmJpC_!uK!XJmb<(tQ5926(rv z3`1>$dJP4xKC!}GtPI!qy^WIC3GatQvSQGXN6Gy&uXV=4(@Cqx6zf2@oFyLdh*BJ+ za#Kjl;?i!hD2evhg334uKHe^#sx#{H|Eha(am)7B2LSPv3{}1Z_uUnE&7^Q)kDutOnlZA2b-_o*;FV!Ycc{M*DehYtT-n$m0o{! z4T|rki1nGgE8krQ(%Nnhb(yPmnJXRHGc9R2g$>OWJE)YK8AQb1mwz>$+kA~?-9F^$ zl8Df%i0iWKzQmRpAt(@S>0$xJ`cLugaZ&ojR4lO>_JFhACv38LdiiK7Z_Yepb3z_= z1wPr;Sdz<3lFK%5bb2cXZo;hx-RLPwgr1>;XWxr%&;CgKQ3uC;8KUke9Q!uK!_VWv z|75G4Q1-c9K&mWuGn`%4qA(fkoxsh8@AmjulN_E-6jz1+!5%itb#HcLf4y*`vOOgF z?EW5BGr>{mnq&0B2K~@kKzE(}E&oA~02XJglv(B8`i3jugU`sv1RO~2bY_WbOb!!` zosEV=QHlN#xBE9?crSwIUzkGmyAY?e&+8#9oe`o~A}8A8whr|_Ei6jvLtCEV=R37f z6nRK&+KqqSX4EGZcyM|AEV2${VWbJ%wSD}z9`r?+GpAf+>4oIzFTZJMOeJszZWjd{ zcItsRHAe-;sLRo6X2WzF$Ik|bJ}27-gyPkmLop)+RJi~YRU9u`rd|6zq@IX@C5>to zSehS_hx$6`F|hc-*a391HS_Ah%@BR~))dohC|&uG&}10(bpH+i>q~DaFRR^UNiIuw zXa&^&p_u@rViFC)E6}b%Ktg9Gw)oIDImXWN-7S#Td}^<<_Q@CUWR#Ay2rzTbEvV<} z?E)>n7(rK4l#S5x2#z^kbv%L^jed z!^=J`f0=nm4OpAZ_+O^PWLXbk@v>!na&}7AvPz4xuU?~_HNV2Ji+6>g*n}_lGHt?D zrJ#@A0z*se|9!#o>ir>eI;J~(xkQuEb4`h2l5wxz65aV9<}maG1(M}?@;?mBLXX%| zU3HWzUUF)4_Z5kM=^W2M%Kcoc7c;(;4fcEjd<6zZVRtU`R4+&l-LZc-q2J{$2vA(= z62JDi-|9BZO8PZ*xUfJcu5Gk6Yavimf_mbE5SJ&?OGLH3sd+YBOv2f~kGmw%W-{$b z;ca*w$X4R#f}25MPOHl`jlMJ9hhEpQ;Pflpw{3xLgRAskiuV%!4-0}FLp*a~WtHW; z3DYL|C1QbKjPM_gS?!@A*YEz9RRWwf{-BZbuFEeygocA^8+|c5`X<^3b)d`tP4v3h zM9+a3m9{uxcpGmJkcaaqN9dEx}R)?V+?z@+{Di^z62gT!u$((gKEFtfciF!bgXVJm2unT+>~+f7bzj(@H~ z-AfBa{graDBk_GmU1KWCtC;cEccy3XkCDO(oVZV~c2!*f7Ib|i4?V-A%amRHJx8Ub z<$>9mUWPqMj#Q-hSj?lihufz|#Mhnx$E6kTNZH>4i>O^K|5lD+*^srl>mD7Lo5=S% z190or9vmBl1~BdJ?dqU;GBkdIbB2C@SId4t)u$xh*+*$3_?+H|Q!h^)kHi>GeDRW7 zgX`!+!tHp~v8i=`;-`uf_j~u|gWkmvFy7ARa5T~Gr~`eyc8pOh+yU(bsFnY7Y<39x z{zP~!h^1aJu`}ENQ2Yg=fOa=_!L+z5mw`@ej&0K4nDNio0mQY-(!lSqx@y1OV=y+w z(X_7|dcSD$zAtVmVu9$7Mgj99mlEQNfvV)%A@_3Dm9rN9^-KsFlocaH*^`oE=bR}t zI4#py*Xi$=TK#r4iHaw*G~W^gckr;_LNRletD5OuBA?4lbm3)Q`=abmJyfcEoKwnXu0GbXc#*@k-%&S3@C8<9sj3(D2~o?>vT%D@XF?UY6puimp z&-==Mb-4-a{6xsh-9}+67*Um-x2ip1>ag(o^KL}mih^0=7&va@tY2SCrpC5K8t0tV zl$-0A64V*Dc zre~o1yZx2P0w)xx3^;IWSDQ67_$nLPTXXYuY|5cu)@u?PZsjXl=g-uoBLJe&%x=kD zs@q05VN3ir^^LuK1E8Qp-}&alQ0g@V-(s zNZA_uVj_cUK~D9=*CZ^^{#~iG&y`tswR3F?ZfI%TR(}?Dbq9k}?se z?YsQkiU@m|(@Cr$JRl`af z3ZWI)q|g1~6Vbbb{m)t+X9rnBQNrIsJDs%XS2ArvZV0k-zWLh?3sTcZEEQr@?HOX^ z1sF*L_%hXN1rpP`XYvzL?pU5_a-M+mj(lQYeJRK_*IA#yW|{fY(_3qdpUM(YqGXii z*^6tj_*ySR!))_&2Nz>=I8I7*6W6Gmen^XY^5jBmF=c8G_joT0%{z17>AQ;Eoo58- zGHeJ(oO-?u+v&Ns-6G3FZe2~;mGqRpC!nLvothq1vZ}p!sgVbYf<}SQeEt0Uz-~=} z@FxGQ&Hg{@#2^kR3Z8@KY;g*JkR-on5(KIEpz@pzRSg&F8TT5d^7%vI6M|u(KKh7N z>ui083{yQ~vOn;rAH)X0%NX*(_9r!w)j?OGo@Eyz_gbaYzy5g5xfUDBU04NFy9pKR`B@2+hN8!# zJnU!zj8Oyl;Yt(~C1ev=^-;5za(J!6lt&*SMh>WOx0s(uGe-)D|IjT|EKO&St=*j2 zaM}!UiF59>o{Z)i-n(rRmdS56XnD6-<~-TRT;N)sz8_?_zY(-uoi1@XVM_hyAb@_A zy$+k35CvmuB1@Wu99B1xEHc)K!(3VbJzQf;wc+^|v;VI9i2>rmU8?dWPl+pC(UTtrjWfn28a5J_k)+h(a$ z6MBB3l`6XS&2e`$({m?MNwmT=p}_oRDlT`Svmd7sNzOZ-W}x)~fSTzdhfwp21B&|^ z#FK``)&sHBLN1amo~z4fjlz6FB53d-FbdZ1U0~YTTaR^pt~G-y=O3UJOK-#8+As-} zqB~YS{2Fr8xDwW-A5YJ{#HKuli#gJ2<=?+!Q=uQXR5}kwSS8&bh5~@S`-mRuvp|Vs z*kxWI{N@)^CJ;0hP09Gg5G~Zb@=>uH>t*0ftnQ$t;Gw+8U=zohbsEkB5D05y6fN(t z-ta`;v!yX4`m%c0y8pYjO%1Ps#*&?vOCj|jylm1VP&EUFfye@$!6hh%Qo@+ob@QSj zk3N9A+-N2bcs$>FR@DdalstgU^Tenp;h*!el3$u+orprTnFmF7S4g72=^-wpy)l|h z9+=heBJUqw2eYhKn1%zc%k0mfMGQpO>E5Z)8onGbuar!-h((lNORevSbU6-AOoRP= zo%MS@AYW&LMck>xuHV%26i8o>lE`H9@Cy97LkbSjDt43P--05`p;n4=^1SswUP1vm^fX%2`MI*x&180>#hF+d#w~1Z@;n zB#hwr0Cx_u3xv0eHwDvHuV}R$Bw*z{;7kG>FJvO_2@rfk+N|#1s5CAvPj{?QEBie* zVN`EIIUHdI+9o+@qZOD3&h;P|V_{rzAy;ugskpjyxaa<{%>PnA(?SzN{tFxZP+&1g z;D}W*Z1}pUl+wT$>4Jk+p)}w~T2CK3l69ccUt_!t_0*~ci@fOS%i^nP?kgO1bVx0sfW-KS)2#hp-m$gS#(382BvP2I)% zI=wVM`&X=Jh>%B|{{#O5BoLkq5P17ZG5qxQ&-9-DqrpE}hy-BA6VDiYmSw!~=Z$jU zhA>B_DC#R7Fw~Px>;SZDg_{btqs0Cn#&8a8h1vwM0Y-rP1TIGaepY&1`Mk8^OJM;K&mr2qRO#2rCFdJ38bs!TUTKG0#ckvTJ zeZ=#*Ip6%aOS@mBhT2F90LSMiI~@e<4*7_^`u&X+U(V$|5bDWE#THy~<y^Q>1FronjvP`J16CB*mAZI#!tQ=j*n^fdvR7?>1>A0hbp668)b8f1sJ#p>K=;2 z^(jNQCSOpN#7qh)U$;uwCDo{?>A5B!qyT66JMzi!F^V4xP51&GjM6bRhnYV=g9V0+ zu+B#3f$)J)Yrsifa?>AThrNApF>P;<#e3M7Q}{G1rM^p6mp6S}+ToHc7a4qszG9)r zVkVpw-23HeSUR3zd?ll#w!l*f6xs@Md z&J#Ic05}T=HwEI3bHI2sZQ64&hSELr_LGQ+CJ+^`gU*&O)>uf7d!Q`MKKP;Wtqjo!GYM zYnM-J@mUwGFjAA8b7~aD*bbpVSeZXD30^-t67ns^G-*;l0lwdXd-}piqqi#(6TQ;g zZuusyU+@wHKt!(`RcvEUf>OYgneYZcczrB7W}e$6%;%xX*UagfRZJog7j>UMZKkkx zET3Hz9vBeB)clES@LLn>V_;D#b`gXuyM1f^l_QM7lRuh@YDJ7B>A+5+`IN+@vMj69INTh$!XeTVSjPcF4?2&j zHdVQqroMf<0SWw=)B=omh;p028Sjy`^0L-awAH@NOJFf_@V3dps2%V*cD@YzcAD~%kJX|^wo_9$&z}}-61&2 zpy8A`M{gg--tOGk>9aJd?P2=*lGYf#eX?c}i0MvAG?am5z}T1hQm>y;u_Z*7UMN&0 z4VO0fCkkpHfueGIXq2ryT=_dwhd3QPN+||>=JT#>Vcw_sPb7T1o|w{TowOon@E%aQ zr72(U=KDid(e(g0z5j182OO2UyL^dLC8sqSE$Qc~>D2O*M0d|Wr2k>*ddurE1&3yp z&voRCH_NtEpH05yEC1KS_p^ETF0rQ~$xEMO>GNjhdXTzUBej2y@`#Ytcum#eA&PLI zH-sYl-3KiB~OGZTQHVc;lrk zF=@?87alb;{N2g7J=?Rrl73_p__)`5I6 zV$Hvu{9_?Ggo>IlFox-GZ(arJ`N-p=#-fJh^*<11cRlDo5)0xZTbZ#$%|M|0Zv_m* z_5p?p4{$_I#OQ%w&k17skO%&Qc0SBB`2Xh-bU1`+XZi&m{MbzBCm@#2Hc7^GUli~T zp9a9|{=b1|dMHE?C4JXOD|+fr69M=sKkW&~ft!DZ-+7;xsupeB=*#F`Ei%K0dy`f^ zhi|r)ciXXS(&iWw0iF;w6a}D70XYv+qHVRSiD|)YX#%Z?b-QNVaUjqG@ymQ6deAXG z#;wX>f<4rxR!RDLGj#%8I>OuXzEO=s{VF?Kj|8o!QMJLC@AJ<(@`h<`7$>US8Hv4k z6VpQ#dcY%}{QRGRf|Sl9C)>_g=IZp0F6OBvsKus7^yJ`rx%c%V=xQ6_E6#UosnRUE z)fs$_SLS#Ggehr^Yx+#0@_pAMG{tdkA5O}Y=UgCTDkc;>YUg%{j`<`XS2QIQ&pXi zeb#tE#=^8&xO*d;RMg0~-#(&EG(?yWPHZ?JWN@&Yw$YJuUv(uc6r)-Yd2^*yhu1c{ z^G7CI%lql9-?(+d15E0bXmdIw9>Ji0qrGlm3lhZ#wu+>YypWp2*=R^xKZ`_ zlY^q0Bq_VibDDfC@Ph~od*Y1Dj}4nk?vnxh`;-&fyRG)w_JggVVg|L{@8?$A>B%n` z)a={UU;`4F1+j|R$l@$P042*BwB1*Tj)^bme4iTz9PVG)3>9pm_w=4nUjHbAZS*j0 zN9M341jAFIObjq@epGZu!Ajj0u!?$HV35$mtxT`N5lUYzWYJUb4WB03aYYx!T6m~1 zN?GilFFaElo%x=;$g`x2>Kqq<_I`CB!zK!U8flXU(AI<;3v0a}I$nHYaKt$8(kP* z3m^A5FBF2u^x<#i7^DFwj)V%QDOofmtB=fp*%GZt>%uzCs1m*e@fb7f=DpN3kx^>` zRZr2?7n((H#a7YpJSaA~(I25~&I|}29DpUivIwTY)!h-X`mSKQ&bXzz-L-lC#AVxx zmCY;UtK6iiO!_S$D+XU*<%5CsyfS3@+1AW-eG^BfMcGJ7#T2Q;=)FkZ?9Tu4Fb;g8 zQC~1-c<2`F<=ZDWKLXhIU(P(>BmZDJJ_G=|pYmZtr2>$(<#>+)zS^}8Gz)0C|K)8! z<+9Q`tBS3%10=-k%+@agul5)lwYyF#D;Ru23=S2QCi1aZ2l5+qhx%&%jYHt9X zbS0McnM2FOzqqKc^Zt-)7XL8Ohc*r1yGB2)J%44_s_82?>O4^{OSj=9a? zBt)N}K-p4p+OO<{FWWi~8~?|55IPL9SWAUZs-qI+Dfv?t!Bb^jHf3#>7)#)vmP#u^ z$s>*_T0l$zQppF!CdyoaW)P@RT6HxnQ5XeF$^Ngv7w|hp0DbcjpfRK6T$ukekNFcH zWSc&BNEDJp=W(f)^k4f21Z!6WIIKjM4ZGFpB|a@g?b6Cbrk0KikpaS2vteUyd{0Ts z=kQ{Ss*difwIHjCUGW=An((vW>XLuMQ~e~sfPW=C5>yg>#0A!2r&xmXU<`#^vT=R1 zhE{1z{*{uS#H>utzbVaxaKsW2NAaz8dvcEE&0p6=^qhEGXutj7 z8gUv#U&+7fK%2L+#J7+hA_}mVS}tq*Mbd@&rFpd8G*Cgxsf#*`jiC!0X?y*I{~%)rB~X5%u|oCtob8+J&X)=c@(mRisp%n}_P|xYg;toy#`IlX z?lVd{=e1C&<$bQB1J}0+L-5GqgR51GSFHk$16(RL)Q1H8gTuf`yCf<(bhC@~La+}8 z_GYFJ+fs8s0OFwUpm={QuHD>8`l*29%MTQQ$FUHXTD!OZuAU9rVN5^hCG$KSl-n7USpGp&J)vapK_oZbF*nu2o z7LRSQEMc~u`Mp5lmiX(M*To3;l7T1O_17Mm&|o`c*yzY8(03mQXOEx|7@`VP;b!m2!Q zbBVn=_J^HcYXIE}T3{e}mnp|GC0X!JjK!CH`A(6Gy%urgsET;hM2xsbq_duKNO<<2L}TH?B`9gGTo9OJw!fWXn(Ua$I$yE zmGkpN68wX(nIi+kDE~BN<%ri{YzOZxeTYH)e>N}wAJbDB;RrAHeLmKz(T9#bE8ANg z)z!08UQ<~U511znU*6uzw)LN6fVm!g)-M;+SLE+;Jon?$@uD9xM{0Kf4C$QQt0W5^ zM)>c*-nBLoD-Bjo+Jzuq7yscrnROxjKU$cGB}BM7aKG5goRTydA?Z#Kjy0Q1m>?ih z3~qZS7&Q}`#xO-iDjOrj1*fB8V!j3QBEPdIp}E#u$3&X#UTPc}z=AHw#c%Y|_Lo6*PeW$MUME#+l~+5s9b=A?5HRWpD)vD>V!w+$<4eb*M5uZ%j^<98;|@tV!Yu34OxcEGk#`d<9Ag?b~3MBDQV2vw5Hvg)cr1o0|XLJR1UE6ds0I--Bzf3fwhe5 zYQg@Z)7@eWFxi%G(Z@jW*uxX0Pk5Z-DU!DPupG>38gC;P^1*rXRgreEhJ0@=Lz5HR z@O}aUI{O`#P+2ov+R_>`-U6fb=9jQ|Zoev@n@xQ}B7TKFeeI{o}6B z-NUR{(Fdrn;1g`+k?{ZT%xI7(+hJH^_s{}$#$~yz)kKz8TWat)8vrC!u^6VAykFo< zPi`pHeiPijujkM6n->nbty{J=8JW0S_64L-5F*f{3#J``y?e1zLyXR~?PDvOn*}i7 z3DOndbZK%>1BS^)V?C4GH)`4C$)rLh1=29N>*b$8&IhzO}ngyTnT*bIpyZM=R zxz8br^qI}^7Q6+PO9C&_^|lVFcI}vKyfM~l+xaNWsv^~pT6A7_!CBEoTKH9!RPK^!$ z0vJE9L{RFYrq8|yG;-TvmgditdJTv!WzTc;* zfy_T?f~o_u_J{--4tbPyP`)83-vam$Xs5;3JW{B0$44HBUE?XY{0!*TM;-?B6iSQt zGmaP=&r2HRk@Bu(GV0lMBrQ^Rbw-O+XUM8MFWZHMgRO90k;F81yjG?vPmoX*?aTt~xW} zKFBgFAsQP~XKSWoY1u{)g|75*O}tCWULqyQ zRql6-S9-;1WoLwVF6Xu*=D$xrOn~hpo`nr+qXsI29-^j?-B^8p!1dMNUS~rgl?)Ib zo%_2ke8TvP8d0wC)_v(j;fUA9^(TZG$dkiUd)j;yKATLsHSh$aM@&JBz6}7R!-Bjg zhgD^q&l@QR+5UML4?{lq`2RYI4GJ6K&1#{^Lp@>znJZUSc3H2R#;7AYh^Kn2KS&QO;B4 z5wb6iOkn=J?#nuI1K)3-_Bm>Es|ikX0`+3)$Ms@C1Nw;8mNf^4ksWIny_pUE$yb`6 zp8`p%%iZtbeKzsYz$_MjC*6?yN=U@!_mQdIl}4Y$4?Zp`n=LtRmRPi&;oeeIPB%ZY5Z zp%n^M19C3v-F%K;qIh|?bZ5YEwj|5+M>$d(4;%`=&a6tcCfl4=mibo#dGW2ne5WjF zGD$JhK1keB&$_gxG*2J$16G(;R9{6KsGL5L+zpg!X6HM;Sevl|2d6m*z)(RgFV&}G z`sA7Ka$1f}Yp7ANAZ4Q|54ax8RQ8YfQDMg$>gfcyItMxzco8#FTJJ|V6}B=k?2?W(dLF7q~|r4TsJzu6~F zGeBqtQ`4gQz_d3u5ca5-0H<{Lg^{0W*nSXG5yBuMP1cuwWj@PBGY>~v*ibtxS`}K7 z`@WhL2Q$I-c=T_H<-~fnmi4)yHJ<#+a#D2+6F?f=DJyZ_NVJEix&;4B|I#ySVb|4c za7&`qm0rkD1eW!Sf5U6UYap@oIAC5#bGG3u3y4!(Q_P-Y?UUI8m^NrBfx*`+k%2mY z8`%%!-s+Kp+ou!n8e|NA9~{Vs8BLG5?@@S`v6O_{7mM?jJk)O<e{Rs&i7R?a{Kez`qW>%hNid9#xnA~_4 zWJ{)Akz?U_DF9Tk?63NFZ^XrY0JC{k zu3VD)RHrd}3OK(%w-616se~TPX-oaFBN(uV@Z-uvGJ4o9tw(3bL8{#?8^N9m=CG)! z;Gh<62>QA-3+=Zyg-vk4>BsY`WZCdv@~Vf3Se19*qXSeYa8iEd*7Vt+?!CX&sNrsL zAdX*m>Qi1KT%S%4q|a-LlBe|}}41%#QYjQ7D3ze;!h!WK!Tg0#S=e>$GIhUa)UE3PcUp_NOe z)m3lbOw1hxFaki3?Zg%VoLhalB zY&f*mK!Q#B!=D@b=WU>r1r(^e^oE@H#_gXA06@=qfFJ%_Hv&rRf4dF&t>dVa-(;Fn zG=lIH6U~GUbKFk-!r~_1g#twRR;|k~4qJUS0Q4IB5y5p7iRGfqK1|~I`CX1ft^)If z;N>-72tU85aY%LBb)1R>vP*teE*-Mh0{_R&0427yg1pzih6CvG_W%GcTD*trS2g!P zI{o883k+fjz27IaQ$@X7g?8Ovkq7X?V)DxBmN70pFgD3kK>@xMS~BgVEpfXN!-*_i?@b3~4ak$KU5W^7837SfDwWS2cFM8(-h z+x&_H6MYlq`Q=Pl9BXnV<@el7#=p9O&z}6M_iBcEl7KAiZo|Zrr$ZNL^y5>784ykk z`SQ&dzhBN-DPFe@uB%t6FJjvWduEh9X%}XLN(0LhWLn#xSS5rjc*)SqI)XGqhq)O! zx@0)`BvLm~w4jBo-A>?LqGSAhDTL$O;?~>a@5;`+Wn@aa!zAFy>2UK03OMaQbBq8_ z!fAgcCiD?a@hy$%o7(Rjex80go!$n)d;+A#+U1C14gtnl>k#*b-hK%-CN7Muj%VQO zStJr7iDM?QH;zAum|xV7OtUsjGBEwif;Ft;yd~cRWpBLzuheAiWD#9{`zDiZ!V-;g zJb`bJ?R6&{#MPA?xZwK;gR4Q16 z7bvJ>>Jou4$CSJga4NX!NZ)Pg*s8KRl3=4OhpTUY*%26;21g9n)FrVBo5ceV3H|lM z>PPgU<#IbJyG=mQ_y)mH+=r7qcU7eF9!pAfbOX*P$s}g3DV3H07hlq1MaP;>k_=>wPafpPPLvzu<5k&6-c>*(3S+>4~O- zVc&8PG{XMF*yq!7GPk|lGjNaj{U$rAX_`4!WSSVZk@o%CLFdRit-C!ZZm{~EYnuLy zfJmPoHRd8;8NC<7h+AFDAjsMEZOFS}=*Cuj09?Gu+!8{VXq3h>nomgD*V(raqsa#}dfSU|207ynKK@0ZpLg&Z-xl;?&X2=;Z=6Z{5&zemD1SKyM zC+I$sV@q*dfNie$P!B;Aj93~~+ZHP_b50g0q$R&QIvVM0VR)G6-}4HXIF}ZxwHqkU zE(PdhA)9+sxYfDdh!mX=>2U<67TgJ6^Tc=-};bC0DB_IAVI*_dPd(D(SGU9NR&?qg-H5W?Tu}hmf6GYS<{@Q@}JxC+l&(FUdMN)s*wSyQ@C6JA%hcJ$Eux(!F zz_x%oOjoGFMgOso7O8}CJ-}yE=tATgl9k(ngoft`qn^&b6_Rz{&f{ZCc1FoRDoo$% z=vIYm@{`n`Sv_5vuvTPy4@T*_Y?Od#YrJ~bZ1976*jq2x`5!x9Fv-)57O?p5U<*a{h(k`#t@QJuJJ0j{Y|1QDR4FC$bc$-TIQ;r0 zqxAVztD(T8FmnT&jE*QD0`m%WZ9(JNqW#ey@zsEoWbtvcdGbTzrg((Zwu-&!oMp9d zj0DS;mwsD`m3?P#B#Ks>nJzIlXaK1_`cKm}K-^PQNa*iWynudGoZJB4p=Im2A`5T3 zYEwW;MG<{pA7Qee86;ow(T=N@!ZG^e=9PXOy1XTgtF|-2o9Gq z_mGK6&?#1lC}?Up8*E}*CcldmV4Eav22Gebn9=B_hd}VrCVPFEMwrs&6x+}h`;X3n zT5t4-A5fe)pF#4^{ZzSO@iX*SI=e_gJdHEyfd>@&aJ2Q9gy>svHUOuc9%+2v8 zRwIVDB`?a*Qbn=+9(fXN3z?oDI`JiYojsP1Q=|)0r2%sJSWpoYu+T<*1$wHuHs%RR zt?B(RJ?TQA{mG~(r?mNIs&hDA)0!zZV4!zvZoho_&jIz6j3Jwi?MiWLdveBSt7V z>_7p795T*5jk-=xYcvpuAZ0!u_|n*yV{&dksV8hqknz^N5&@x7xOD~2Ecz|<($eoU zV;ayB@}ck{yeK}mvKfD9#(Tde4Q)3uTi6A?8fu;+ zlMaqB=ExezxUEEpUYElSVLhC*UHQhXPg{{V`6%lKGRd*wlk5cGqs?j;kdrhRr3cn# zLqvk9ajI`HkdSq%eHtJL`IBintHEZs>03u?o?4OMT@9k$8Svaem%cLLQ=ZxSQo0tj zMIA_0|D{+Ni_@;;uZu#>02L_dkp$x7pA?Y{ zPo8$MRQuEU>|X)cq(>F*6Lv}bl4(zl!ufCFj_lffKF-C4C%5HJz!3TL;q$|CV>N-v z0&aINUwN5xWPTU^MG^eFw}{=S->(P=2vHs$3IQBCF{@Wts^%f&N{jHGnhup4OAXDs z3e=m@tX`N6b9j1iDeQf5$ZVig?7`xP;I6spp@=d52dErjxF=^aVLqG8N+DP?-y$%S z8{3jB(Pj;iem)1&l`#B~{bJTJk(iMa$xPK4=>rjUZHk#hGuzp&`Uu` zx5WROeCqZp+*AIUaAF^g4FyG^TRY73c2pvsI0Ah%Scfopk#aUSHRD*Ezmq|D-JF(n zrdV9$F%SMr_O??l}+}h--FcvRc0rj=*J`)o_yU^+^CGMICdr(Q@>*<*t@-x zf1u)aqwBTBXokfDM79mdNWWNdj$qPJq4dsMY+!D|>@3#B20}1KYdoA36H!CowZXRadZ96WP(57BQ${S453m zJl7nJ8Z)gzSO5mNiCRhMNt*8RpSE3zMay!S%tIP5nR%<{)L6kmAz`zmeg+B|~e2=6~O>Yl&RUyPgJ_*|V(d3UWR^@vO)AN2I#D z;EKQV;eRa?^^er)xXPz5+gI39v}*9H_85 zmgY6gQ#2?Fl(LAoypRAcCa0e?xl^cUb>oDhOX@JdR^5b94>FXTX^)wvGCHGSOE|mm zw3Pby?rA1CyGAMF=pS~*7V(zTpD(8fu9;tK7D#5eJH%r5J=q=_qOn%N3XKWZbu%v8 zZV)v|smyY63)izRqVm?go;de%ibM&@WT9nfIu^BvCH3+kPdnS{V>rw5MXxb;4kyer z+`zV{QB6;`Zh192emF-y$)xo?@j%*0cwHy9CIuez$TA}A_>u}EzAv2+RAl&O$XGdH zmQGtFQEquHfl6f1LzWnd&e#XH&+eO^ zlhIr(IABl-f1aKMui9UpsZr@m3F#NUi8u0~Xl7Bhv*L!)&t~b*=O_|Ob*9REzl+AB zz;0|RHrkilAR$Z3ReKnIj*4j|;Dvfrq1|0*Y;@?zqYB_mr%rZkTs|D~q00y7K)sr0 zh~UChnmX;uT3jHJp$zI0zgcetlVJZ0--t}y0`Y+g32V%ipl^3v zN3Ls;y9I1h0l+-XHd@F3jV6bh>ikVJy` z&ct&?NAMu`6=ki4zxQ*tjGs5M$G&h6iXHiYfU+qB;LObDW%zSE>`)N>q8xbc_;&ov z4lbjr<*4`}@O8gW=%d*`7vT2_mrJn0ee0mNXDYS#`w~Q%YHdTN_J*=RfY8(0k2^Bh81R6r=ckhHE-0 zH!Ztb<|LuXiXs!w@nG^FyIx#aepfx_W$ab3Q!#MrQZXO*KJ+uZg9Ms%rF03SyTgMz zs5njLO&_qP9#sf2E6*f8p<>Eq@4ah#;3GF*+7F6+J}enUpnsonxl}N*0cke`!Mqs= zXxTzh*DCLn0B?`(4_XS>TITKS+H2o=%ssy&dW{{+2xC{XDsh}~rtQd%SiD<`M1-4O0K-fGeN#Ri}71w<;*5t2RJe}G_f_Ks;)wRC2{TOQYz->%NRKV)U%F;-kD6bE5G~&9)43T