diff --git a/Diz.Core/Diz.Core.csproj b/Diz.Core/Diz.Core.csproj new file mode 100644 index 00000000..1f8d895d --- /dev/null +++ b/Diz.Core/Diz.Core.csproj @@ -0,0 +1,163 @@ + + + + + Debug + AnyCPU + {50646B00-03D8-4CEC-9ED1-3EF4CB199E8B} + Library + Properties + Diz.Core + Diz.Core + v4.8 + 512 + true + 8.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\ExtendedXmlSerializer.3.4.1\lib\net452\ExtendedXmlSerializer.dll + + + ..\packages\FastBitmapLib.2.0.0\lib\net452\FastBitmapLib.dll + + + ..\packages\SharpZipLib.1.3.0\lib\net45\ICSharpCode.SharpZipLib.dll + + + ..\packages\IX.Abstractions.0.5.3\lib\net472\IX.Abstractions.dll + + + ..\packages\IX.Abstractions.Collections.0.5.3\lib\net472\IX.Abstractions.Collections.dll + + + ..\packages\IX.Abstractions.Threading.0.5.3\lib\net472\IX.Abstractions.Threading.dll + + + ..\packages\IX.Observable.0.5.3\lib\net472\IX.Observable.dll + + + ..\packages\IX.StandardExtensions.0.5.3\lib\net472\IX.StandardExtensions.dll + + + ..\packages\IX.StandardExtensions.ComponentModel.0.5.3\lib\net472\IX.StandardExtensions.ComponentModel.dll + + + ..\packages\IX.StandardExtensions.Threading.0.5.3\lib\net472\IX.StandardExtensions.Threading.dll + + + ..\packages\IX.Undoable.0.5.3\lib\net472\IX.Undoable.dll + + + ..\packages\JetBrains.Annotations.2019.1.3\lib\net20\JetBrains.Annotations.dll + + + ..\packages\LightInject.6.3.5\lib\net46\LightInject.dll + + + ..\packages\NReco.LambdaParser.1.0.12\lib\net45\NReco.LambdaParser.dll + + + ..\packages\Sprache.2.3.1\lib\net45\Sprache.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll + + + + + + ..\packages\System.Interactive.4.1.1\lib\net45\System.Interactive.dll + + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Diz.Core/Properties/AssemblyInfo.cs b/Diz.Core/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..49e87f75 --- /dev/null +++ b/Diz.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Diz.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Diz.Core")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("50646b00-03d8-4cec-9ed1-3ef4cb199e8b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Diz.Core/SampleRomData.cs b/Diz.Core/SampleRomData.cs new file mode 100644 index 00000000..7d866f1d --- /dev/null +++ b/Diz.Core/SampleRomData.cs @@ -0,0 +1,206 @@ +using Diz.Core.model; +using Diz.Core.util; +using IX.Observable; + +namespace Diz.Core +{ + public class SampleRomData : Data + { + public static SampleRomData SampleData + { + get + { + // one-time: take our sample data from below and bolt some extra stuff on top of it. + // then, cache all this in a static read-only property + + if (_finalSampleData != null) + return _finalSampleData; + + // tricky: this sample data can be used to populate the "sample assembly output" + // window to demo some features. One thing we'd like to demo is showing generated + // labels that reach into "unreached" code (i.e. labels like "UNREACH_XXXXX") + // + // To accomplish this, we'll pad the size of the sample ROM data to 32k, but, + // we'll tell the assembly exporter to limit to the first couple hundred bytes by + // only assembling bytes up to BaseSampleData.SizeOverride. + BaseSampleData.OriginalRomSizeBeforePadding = BaseSampleData.RomBytes.Count; + while (BaseSampleData.RomBytes.Count < 0x8000) + BaseSampleData.RomBytes.Add(new RomByte()); + + _finalSampleData = BaseSampleData; + return BaseSampleData; + } + } + + public int OriginalRomSizeBeforePadding { get; set; } + + private static SampleRomData _finalSampleData; + + private static readonly SampleRomData BaseSampleData = new SampleRomData + { + // random sample code I made up; hopefully it shows a little bit of + // everything so you can see how the settings will effect the output + RomMapMode = RomMapMode.LoRom, + RomSpeed = RomSpeed.FastRom, + RomBytes = new RomBytes { + new RomByte {Rom = 0x78, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, Point = InOutPoint.InPoint}, + new RomByte {Rom = 0xA9, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true}, + new RomByte {Rom = 0x01, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x8D, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true}, + new RomByte {Rom = 0x0D, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x42, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x5C, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, Point = InOutPoint.EndPoint}, + new RomByte {Rom = 0x0A, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0xC2, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, Point = InOutPoint.InPoint}, + new RomByte {Rom = 0x30, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0xA9, TypeFlag = FlagType.Opcode}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x21, TypeFlag = FlagType.Operand}, + new RomByte {Rom = 0x5B, TypeFlag = FlagType.Opcode}, + new RomByte {Rom = 0x4B, TypeFlag = FlagType.Opcode, DirectPage = 0x2100}, + new RomByte {Rom = 0xAB, TypeFlag = FlagType.Opcode, DirectPage = 0x2100}, + new RomByte {Rom = 0xA2, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x07, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xBF, TypeFlag = FlagType.Opcode, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x32, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x9F, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x7E, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xCA, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xCA, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x10, TypeFlag = FlagType.Opcode, Point = InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xF4, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x40, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x41, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x42, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x43, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xAE, TypeFlag = FlagType.Opcode, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xFC, TypeFlag = FlagType.Opcode, Point = InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x3A, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x4C, TypeFlag = FlagType.Opcode, Point = InOutPoint.EndPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xC0, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Data16Bit, Point = InOutPoint.ReadPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x08, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x10, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x20, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x44, TypeFlag = FlagType.Pointer16Bit, Point = InOutPoint.ReadPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x7B, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x80, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x44, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x81, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xC4, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x81, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x0A, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x82, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x08, TypeFlag = FlagType.Opcode, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x8B, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x4B, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xAB, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xE2, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x20, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xC2, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x10, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xA2, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x1F, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + + // -------------------------- + // highlighting a particular section here + // we will use this for unit tests as well. + + // LDA.W Test_Data,X + new RomByte {Rom = 0xBD, TypeFlag = FlagType.Opcode, MFlag = true, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x5B, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, // Test_Data + new RomByte {Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, // Test_Data + + // STA.W $0100,X + new RomByte {Rom = 0x9D, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x01, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + + // DEX + new RomByte {Rom = 0xCA, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + + // BPL CODE_80804F + new RomByte {Rom = 0x10, TypeFlag = FlagType.Opcode, MFlag = true, Point = InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xF7, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + + // ------------------------------------ + + new RomByte {Rom = 0xAB, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x28, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x60, TypeFlag = FlagType.Opcode, Point = InOutPoint.EndPoint, DataBank = 0x80, DirectPage = 0x2100}, + + // -------------------------- + + new RomByte {Rom = 0x45, TypeFlag = FlagType.Data8Bit, Point = InOutPoint.ReadPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x8D, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x69, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x83, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xB2, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x99, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x23, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x01, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xA3, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xF8, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x52, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x08, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xBB, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x29, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x5C, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x32, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xE7, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x88, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x3C, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x30, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x18, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x9A, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xB0, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x34, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x8C, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xDD, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x05, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xB7, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x83, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x34, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x6D, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, + }, + Comments = new ObservableDictionary() + { + {0x808000 + 0x03, "this sets FastROM"}, + {0x808000 + 0x0F, "direct page = $2100"}, + {0x808000 + 0x21, "clear APU regs"}, + {0x808000 + 0x44, "this routine copies Test_Data to $7E0100"} + }, + Labels = new ObservableDictionary() + { + {0x808000 + 0x00, new Label {Name = "Emulation_RESET", Comment = "Sample emulation reset location"}}, + {0x808000 + 0x0A, new Label {Name = "FastRESET", Comment = "Sample label"}}, + {0x808000 + 0x32, new Label {Name = "Test_Indices"}}, + {0x808000 + 0x3A, new Label {Name = "Pointer_Table"}}, + {0x808000 + 0x44, new Label {Name = "First_Routine"}}, + {0x808000 + 0x5B, new Label {Name = "Test_Data", Comment = "Pretty cool huh?"}} + }, + }; + } +} diff --git a/Diz.Core/app.config b/Diz.Core/app.config new file mode 100644 index 00000000..72259e86 --- /dev/null +++ b/Diz.Core/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Diz.Core/arch/CPU65C816.cs b/Diz.Core/arch/CPU65C816.cs new file mode 100644 index 00000000..396955b2 --- /dev/null +++ b/Diz.Core/arch/CPU65C816.cs @@ -0,0 +1,494 @@ +using Diz.Core.model; +using Diz.Core.util; + +namespace Diz.Core.arch +{ + public class Cpu65C816 + { + private readonly Data data; + public Cpu65C816(Data data) + { + this.data = data; + } + public int Step(int offset, bool branch, bool force, int prevOffset) + { + var opcode = data.GetRomByte(offset); + var prevDirectPage = data.GetDirectPage(offset); + var prevDataBank = data.GetDataBank(offset); + bool prevX = data.GetXFlag(offset), prevM = data.GetMFlag(offset); + + while (prevOffset >= 0 && data.GetFlag(prevOffset) == Data.FlagType.Operand) prevOffset--; + if (prevOffset >= 0 && data.GetFlag(prevOffset) == Data.FlagType.Opcode) + { + prevDirectPage = data.GetDirectPage(prevOffset); + prevDataBank = data.GetDataBank(prevOffset); + prevX = data.GetXFlag(prevOffset); + prevM = data.GetMFlag(prevOffset); + } + + if (opcode == 0xC2 || opcode == 0xE2) // REP SEP + { + prevX = (data.GetRomByte(offset + 1) & 0x10) != 0 ? opcode == 0xE2 : prevX; + prevM = (data.GetRomByte(offset + 1) & 0x20) != 0 ? opcode == 0xE2 : prevM; + } + + // set first byte first, so the instruction length is correct + data.SetFlag(offset, Data.FlagType.Opcode); + data.SetDataBank(offset, prevDataBank); + data.SetDirectPage(offset, prevDirectPage); + data.SetXFlag(offset, prevX); + data.SetMFlag(offset, prevM); + + var length = GetInstructionLength(offset); + + // TODO: I don't think this is handling execution bank boundary wrapping correctly? -Dom + // If we run over the edge of a bank, we need to go back to the beginning of that bank, not go into + // the next one. While this should be really rare, it's technically valid. + // + // docs: http://www.6502.org/tutorials/65c816opcodes.html#5.1.2 + // [Note that although the 65C816 has a 24-bit address space, the Program Counter is only a 16-bit register and + // the Program Bank Register is a separate (8-bit) register. This means that instruction execution wraps at bank + // boundaries. This is true even if the bank boundary occurs in the middle of the instruction.] + // + // TODO: check other areas, anywhere we're accessing a Rom address plus some offset, might need to wrap + // in most situations. + for (var i = 1; i < length; i++) + { + data.SetFlag(offset + i, Data.FlagType.Operand); + data.SetDataBank(offset + i, prevDataBank); + data.SetDirectPage(offset + i, prevDirectPage); + data.SetXFlag(offset + i, prevX); + data.SetMFlag(offset + i, prevM); + } + + MarkInOutPoints(offset); + + var nextOffset = offset + length; + + if (force || (opcode != 0x4C && opcode != 0x5C && opcode != 0x80 && opcode != 0x82 && (!branch || + (opcode != 0x10 && opcode != 0x30 && opcode != 0x50 && opcode != 0x70 && opcode != 0x90 && + opcode != 0xB0 && opcode != 0xD0 && opcode != 0xF0 && opcode != 0x20 && + opcode != 0x22)))) + return nextOffset; + + var iaNextOffsetPc = data.ConvertSnesToPc(GetIntermediateAddress(offset, true)); + if (iaNextOffsetPc >= 0) + nextOffset = iaNextOffsetPc; + + return nextOffset; + } + + // input: ROM offset + // return: a SNES address + public int GetIntermediateAddress(int offset, bool resolve) + { + int bank, directPage, operand, programCounter; + var opcode = data.GetRomByte(offset); + + var mode = GetAddressMode(offset); + switch (mode) + { + case AddressMode.DirectPage: + case AddressMode.DirectPageXIndex: + case AddressMode.DirectPageYIndex: + case AddressMode.DirectPageIndirect: + case AddressMode.DirectPageXIndexIndirect: + case AddressMode.DirectPageIndirectYIndex: + case AddressMode.DirectPageLongIndirect: + case AddressMode.DirectPageLongIndirectYIndex: + if (resolve) + { + directPage = data.GetDirectPage(offset); + operand = data.GetRomByte(offset + 1); + return (directPage + operand) & 0xFFFF; + } + else + { + goto case AddressMode.DirectPageSIndex; + } + case AddressMode.DirectPageSIndex: + case AddressMode.DirectPageSIndexIndirectYIndex: + return data.GetRomByte(offset + 1); + case AddressMode.Address: + case AddressMode.AddressXIndex: + case AddressMode.AddressYIndex: + case AddressMode.AddressXIndexIndirect: + bank = (opcode == 0x20 || opcode == 0x4C || opcode == 0x7C || opcode == 0xFC) ? + data.ConvertPCtoSnes(offset) >> 16 : + data.GetDataBank(offset); + operand = data.GetRomWord(offset + 1); + return (bank << 16) | operand; + case AddressMode.AddressIndirect: + case AddressMode.AddressLongIndirect: + operand = data.GetRomWord(offset + 1); + return operand; + case AddressMode.Long: + case AddressMode.LongXIndex: + operand = data.GetRomLong(offset + 1); + return operand; + case AddressMode.Relative8: + programCounter = data.ConvertPCtoSnes(offset + 2); + bank = programCounter >> 16; + offset = (sbyte)data.GetRomByte(offset + 1); + return (bank << 16) | ((programCounter + offset) & 0xFFFF); + case AddressMode.Relative16: + programCounter = data.ConvertPCtoSnes(offset + 3); + bank = programCounter >> 16; + offset = (short)data.GetRomWord(offset + 1); + return (bank << 16) | ((programCounter + offset) & 0xFFFF); + } + return -1; + } + + public string GetInstruction(int offset) + { + AddressMode mode = GetAddressMode(offset); + string format = GetInstructionFormatString(offset); + string mnemonic = GetMnemonic(offset); + string op1 = "", op2 = ""; + if (mode == AddressMode.BlockMove) + { + op1 = Util.NumberToBaseString(data.GetRomByte(offset + 1), Util.NumberBase.Hexadecimal, 2, true); + op2 = Util.NumberToBaseString(data.GetRomByte(offset + 2), Util.NumberBase.Hexadecimal, 2, true); + } + else if (mode == AddressMode.Constant8 || mode == AddressMode.Immediate8) + { + op1 = Util.NumberToBaseString(data.GetRomByte(offset + 1), Util.NumberBase.Hexadecimal, 2, true); + } + else if (mode == AddressMode.Immediate16) + { + op1 = Util.NumberToBaseString(data.GetRomWord(offset + 1), Util.NumberBase.Hexadecimal, 4, true); + } + else + { + // dom note: this is where we could inject expressions if needed. it gives stuff like "$F001". + // we could substitute our expression of "$#F000 + $#01" or "some_struct.member" like "player.hp" + // the expression must be verified to always match the bytes in the file [unless we allow overriding] + op1 = FormatOperandAddress(offset, mode); + } + return string.Format(format, mnemonic, op1, op2); + } + + public int GetInstructionLength(int offset) + { + var mode = GetAddressMode(offset); + return InstructionLength(mode); + } + + public static int InstructionLength(AddressMode mode) + { + switch (mode) + { + case AddressMode.Implied: + case AddressMode.Accumulator: + return 1; + case AddressMode.Constant8: + case AddressMode.Immediate8: + case AddressMode.DirectPage: + case AddressMode.DirectPageXIndex: + case AddressMode.DirectPageYIndex: + case AddressMode.DirectPageSIndex: + case AddressMode.DirectPageIndirect: + case AddressMode.DirectPageXIndexIndirect: + case AddressMode.DirectPageIndirectYIndex: + case AddressMode.DirectPageSIndexIndirectYIndex: + case AddressMode.DirectPageLongIndirect: + case AddressMode.DirectPageLongIndirectYIndex: + case AddressMode.Relative8: + return 2; + case AddressMode.Immediate16: + case AddressMode.Address: + case AddressMode.AddressXIndex: + case AddressMode.AddressYIndex: + case AddressMode.AddressIndirect: + case AddressMode.AddressXIndexIndirect: + case AddressMode.AddressLongIndirect: + case AddressMode.BlockMove: + case AddressMode.Relative16: + return 3; + case AddressMode.Long: + case AddressMode.LongXIndex: + return 4; + } + + return 1; + } + + public void MarkInOutPoints(int offset) + { + int opcode = data.GetRomByte(offset); + int iaOffsetPc = data.ConvertSnesToPc(data.GetIntermediateAddress(offset, true)); + + // set read point on EA + if (iaOffsetPc >= 0 && ( // these are all read/write/math instructions + ((opcode & 0x04) != 0) || ((opcode & 0x0F) == 0x01) || ((opcode & 0x0F) == 0x03) || + ((opcode & 0x1F) == 0x12) || ((opcode & 0x1F) == 0x19)) && + (opcode != 0x45) && (opcode != 0x55) && (opcode != 0xF5) && (opcode != 0x4C) && + (opcode != 0x5C) && (opcode != 0x6C) && (opcode != 0x7C) && (opcode != 0xDC) && (opcode != 0xFC) + ) data.SetInOutPoint(iaOffsetPc, Data.InOutPoint.ReadPoint); + + // set end point on offset + if (opcode == 0x40 || opcode == 0x4C || opcode == 0x5C || opcode == 0x60 // RTI JMP JML RTS + || opcode == 0x6B || opcode == 0x6C || opcode == 0x7C || opcode == 0x80 // RTL JMP JMP BRA + || opcode == 0x82 || opcode == 0xDB || opcode == 0xDC // BRL STP JML + ) data.SetInOutPoint(offset, Data.InOutPoint.EndPoint); + + // set out point on offset + // set in point on EA + if (iaOffsetPc >= 0 && ( + opcode == 0x4C || opcode == 0x5C || opcode == 0x80 || opcode == 0x82 // JMP JML BRA BRL + || opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 // BPL BMI BVC BVS + || opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0 // BCC BCS BNE BEQ + || opcode == 0x20 || opcode == 0x22)) // JSR JSL + { + data.SetInOutPoint(offset, Data.InOutPoint.OutPoint); + data.SetInOutPoint(iaOffsetPc, Data.InOutPoint.InPoint); + } + } + + private string FormatOperandAddress(int offset, AddressMode mode) + { + int address = data.GetIntermediateAddress(offset); + if (address < 0) + return ""; + + var label = data.GetLabelName(address); + if (label != "") + return label; + + var count = BytesToShow(mode); + if (mode == AddressMode.Relative8 || mode == AddressMode.Relative16) address = data.GetRomWord(offset + 1); + address &= ~(-1 << (8 * count)); + return Util.NumberToBaseString(address, Util.NumberBase.Hexadecimal, 2 * count, true); + } + + private string GetMnemonic(int offset, bool showHint = true) + { + var mn = Mnemonics[data.GetRomByte(offset)]; + if (!showHint) + return mn; + + var mode = GetAddressMode(offset); + var count = BytesToShow(mode); + + if (mode == AddressMode.Constant8 || mode == AddressMode.Relative16 || mode == AddressMode.Relative8) return mn; + + return count switch + { + 1 => mn += ".B", + 2 => mn += ".W", + 3 => mn += ".L", + _ => mn + }; + } + + private static int BytesToShow(AddressMode mode) + { + switch (mode) + { + case AddressMode.Constant8: + case AddressMode.Immediate8: + case AddressMode.DirectPage: + case AddressMode.DirectPageXIndex: + case AddressMode.DirectPageYIndex: + case AddressMode.DirectPageSIndex: + case AddressMode.DirectPageIndirect: + case AddressMode.DirectPageXIndexIndirect: + case AddressMode.DirectPageIndirectYIndex: + case AddressMode.DirectPageSIndexIndirectYIndex: + case AddressMode.DirectPageLongIndirect: + case AddressMode.DirectPageLongIndirectYIndex: + case AddressMode.Relative8: + return 1; + case AddressMode.Immediate16: + case AddressMode.Address: + case AddressMode.AddressXIndex: + case AddressMode.AddressYIndex: + case AddressMode.AddressIndirect: + case AddressMode.AddressXIndexIndirect: + case AddressMode.AddressLongIndirect: + case AddressMode.Relative16: + return 2; + case AddressMode.Long: + case AddressMode.LongXIndex: + return 3; + } + return 0; + } + + // {0} = mnemonic + // {1} = intermediate address / label OR operand 1 for block move + // {2} = operand 2 for block move + private string GetInstructionFormatString(int offset) + { + var mode = GetAddressMode(offset); + switch (mode) + { + case AddressMode.Implied: + return "{0}"; + case AddressMode.Accumulator: + return "{0} A"; + case AddressMode.Constant8: + case AddressMode.Immediate8: + case AddressMode.Immediate16: + return "{0} #{1}"; + case AddressMode.DirectPage: + case AddressMode.Address: + case AddressMode.Long: + case AddressMode.Relative8: + case AddressMode.Relative16: + return "{0} {1}"; + case AddressMode.DirectPageXIndex: + case AddressMode.AddressXIndex: + case AddressMode.LongXIndex: + return "{0} {1},X"; + case AddressMode.DirectPageYIndex: + case AddressMode.AddressYIndex: + return "{0} {1},Y"; + case AddressMode.DirectPageSIndex: + return "{0} {1},S"; + case AddressMode.DirectPageIndirect: + case AddressMode.AddressIndirect: + return "{0} ({1})"; + case AddressMode.DirectPageXIndexIndirect: + case AddressMode.AddressXIndexIndirect: + return "{0} ({1},X)"; + case AddressMode.DirectPageIndirectYIndex: + return "{0} ({1}),Y"; + case AddressMode.DirectPageSIndexIndirectYIndex: + return "{0} ({1},S),Y"; + case AddressMode.DirectPageLongIndirect: + case AddressMode.AddressLongIndirect: + return "{0} [{1}]"; + case AddressMode.DirectPageLongIndirectYIndex: + return "{0} [{1}],Y"; + case AddressMode.BlockMove: + return "{0} {1},{2}"; + } + return ""; + } + + private AddressMode GetAddressMode(int offset) + { + var mode = AddressingModes[data.GetRomByte(offset)]; + return mode switch + { + AddressMode.ImmediateMFlagDependent => data.GetMFlag(offset) + ? AddressMode.Immediate8 + : AddressMode.Immediate16, + AddressMode.ImmediateXFlagDependent => data.GetXFlag(offset) + ? AddressMode.Immediate8 + : AddressMode.Immediate16, + _ => mode + }; + } + + public enum AddressMode : byte + { + Implied, Accumulator, Constant8, Immediate8, Immediate16, + ImmediateXFlagDependent, ImmediateMFlagDependent, + DirectPage, DirectPageXIndex, DirectPageYIndex, + DirectPageSIndex, DirectPageIndirect, DirectPageXIndexIndirect, + DirectPageIndirectYIndex, DirectPageSIndexIndirectYIndex, + DirectPageLongIndirect, DirectPageLongIndirectYIndex, + Address, AddressXIndex, AddressYIndex, AddressIndirect, + AddressXIndexIndirect, AddressLongIndirect, + Long, LongXIndex, BlockMove, Relative8, Relative16 + } + + private static readonly string[] Mnemonics = + { + "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", + "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", + "JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", + "BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", + "RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", + "BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", "CLI", "EOR", "PHY", "TCD", "JML", "EOR", "LSR", "EOR", + "RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", + "BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", + "BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", + "BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", + "LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", + "BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", + "CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", + "BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", + "CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", + "BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" + }; + + private static readonly AddressMode[] AddressingModes = + { + AddressMode.Constant8, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.DirectPage, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Accumulator, AddressMode.Implied, + AddressMode.Address, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + + AddressMode.Address, AddressMode.DirectPageXIndexIndirect, AddressMode.Long, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Accumulator, AddressMode.Implied, + AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + + AddressMode.Implied, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex, + AddressMode.BlockMove, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.BlockMove, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied, + AddressMode.Long, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + + AddressMode.Implied, AddressMode.DirectPageXIndexIndirect, AddressMode.Relative16, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied, + AddressMode.AddressIndirect, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied, + AddressMode.AddressXIndexIndirect, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + + AddressMode.Relative8, AddressMode.DirectPageXIndexIndirect, AddressMode.Relative16, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageYIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied, + AddressMode.Address, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + + AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageXIndexIndirect, AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageYIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied, + AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.AddressYIndex, AddressMode.LongXIndex, + + AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.DirectPageIndirect, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied, + AddressMode.AddressLongIndirect, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + + AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex, + AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect, + AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied, + AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long, + AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex, + AddressMode.Address, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex, + AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied, + AddressMode.AddressXIndexIndirect, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex, + }; + } +} diff --git a/Diz.Core/export/LogCreator.ErrorChecking.cs b/Diz.Core/export/LogCreator.ErrorChecking.cs new file mode 100644 index 00000000..c4d85365 --- /dev/null +++ b/Diz.Core/export/LogCreator.ErrorChecking.cs @@ -0,0 +1,95 @@ +using Diz.Core.model; +using Diz.Core.util; + +namespace Diz.Core.export +{ + public partial class LogCreator + { + protected int ErrorCount; + + private void ReportError(int offset, string msg) + { + ++ErrorCount; + var offsetMsg = offset >= 0 ? $" Offset 0x{offset:X}" : ""; + Output.WriteErrorLine($"({ErrorCount}){offsetMsg}: {msg}"); + } + + private void ErrorIfOperand(int offset) + { + if (Data.GetFlag(offset) == Data.FlagType.Operand) + ReportError(offset, "Bytes marked as operands formatted as Data."); + } + + private void ErrorIfAdjacentOperandsSeemWrong(int offset) + { + if (Data.GetFlag(offset) != Data.FlagType.Operand) + return; + + var byteLengthFollowing = GetByteLengthFollowing(offset); + var stop = false; + for (var i = 1; i < byteLengthFollowing && !stop; ++i) + { + var expectedFlag = GetFlagButSwapOpcodeForOperand(offset); + + // note: use bitwise-or so we don't short circuit. both checks need to run independently + stop = + !ErrorIfBoundsLookWrong(offset, i) | + !ErrorIfUnexpectedFlagAt(offset + i, expectedFlag); + } + } + + private void ErrorIfBranchToNonInstruction(int offset) + { + var ia = Data.GetIntermediateAddress(offset, true); + if (ia >= 0 && IsOpcodeOutboundJump(offset) && !DoesIndirectAddressPointToOpcode(ia)) + ReportError(offset, "Branch or jump instruction to a non-instruction."); + } + + private bool ErrorIfBoundsLookWrong(int offsetStart, int len) + { + if (IsOffsetInRange(offsetStart + len)) + return true; + + var flagDescription = Util.GetEnumDescription(GetFlagButSwapOpcodeForOperand(offsetStart)); + ReportError(offsetStart, $"{flagDescription} extends past the end of the ROM."); + return false; + } + + private bool ErrorIfUnexpectedFlagAt(int nextOffset, Data.FlagType expectedFlag) + { + if (!IsOffsetInRange(nextOffset)) + return false; + + if (Data.GetFlag(nextOffset) == expectedFlag) + return true; + + var expectedFlagName = Util.GetEnumDescription(expectedFlag); + var actualFlagName = Util.GetEnumDescription(Data.GetFlag(nextOffset)); + var msg = $"Expected {expectedFlagName}, but got {actualFlagName} instead."; + ReportError(nextOffset, msg); + return false; + } + + private bool DoesIndirectAddressPointToOpcode(int ia) + { + return Data.GetFlag(Data.ConvertSnesToPc(ia)) == Data.FlagType.Opcode; + } + + private bool IsOpcodeOutboundJump(int offset) + { + return Data.GetFlag(offset) == Data.FlagType.Opcode && + Data.GetInOutPoint(offset) == Data.InOutPoint.OutPoint; + } + + private bool IsOffsetInRange(int offset) + { + return offset >= 0 && offset < Data.GetRomSize(); + } + + private int GetByteLengthFollowing(int offset) + { + var flag = Data.GetFlag(offset); + return flag == Data.FlagType.Opcode ? GetLineByteLength(offset) : RomUtil.GetByteLengthForFlag(flag); + } + } +} \ No newline at end of file diff --git a/Diz.Core/export/LogCreator.Setup.cs b/Diz.Core/export/LogCreator.Setup.cs new file mode 100644 index 00000000..579b596d --- /dev/null +++ b/Diz.Core/export/LogCreator.Setup.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using Diz.Core.model; + +namespace Diz.Core.export +{ + // setup-specific parts of LogCreator + // TODO: probably should move this stuff into a helper class so it's not a partial + public partial class LogCreator + { + protected class AssemblerHandler : Attribute + { + public string Token; + public int Length; + } + + public class OutputResult + { + public bool Success; + public int ErrorCount = -1; + public LogCreator LogCreator; + public string OutputStr = ""; // only set if outputString=true + } + + + // DONT use directly [except to setup the caching] + protected static Dictionary> parametersCache; + + // SAFE to use directly. + protected static Dictionary> Parameters + { + get + { + CacheAssemblerAttributeInfo(); + return parametersCache; + } + } + + protected static void CacheAssemblerAttributeInfo() + { + if (parametersCache != null) + return; + + parametersCache = new Dictionary>(); + + var methodsWithAttributes = typeof(LogCreator) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .Where( + x => x.GetCustomAttributes(typeof(AssemblerHandler), false).FirstOrDefault() != null + ); + + foreach (var method in methodsWithAttributes) + { + var assemblerHandler = method.GetCustomAttribute(); + var token = assemblerHandler.Token; + var length = assemblerHandler.Length; + + // check your method signature if you hit this stuff. + Debug.Assert(method.GetParameters().Length == 2); + + Debug.Assert(method.GetParameters()[0].ParameterType == typeof(int)); + Debug.Assert(method.GetParameters()[0].Name == "offset" || method.GetParameters()[0].Name == "snesOffset"); + + Debug.Assert(method.GetParameters()[1].ParameterType == typeof(int)); + Debug.Assert(method.GetParameters()[1].Name == "length"); + + Debug.Assert(method.ReturnType == typeof(string)); + + parametersCache.Add(token, (new Tuple(method, length))); + } + + Debug.Assert(parametersCache.Count != 0); + } + + protected string GetParameter(int offset, string parameter, int length) + { + if (!Parameters.TryGetValue(parameter, out var methodAndLength)) + { + throw new InvalidDataException($"Unknown parameter: {parameter}"); + } + + var methodInfo = methodAndLength.Item1; + var callParams = new object[] { offset, length }; + + var returnValue = methodInfo.Invoke(this, callParams); + + Debug.Assert(returnValue is string); + return returnValue as string; + } + + public void AddExtraLabel(int i, Label v) + { + Debug.Assert(v != null); + if (ExtraLabels.ContainsKey(i)) + return; + + v.CleanUp(); + + ExtraLabels.Add(i, v); + } + + // Generate labels like "CODE_856469" and "DATA_763525" + // These will be combined with the original labels to produce our final assembly + // These labels exist only for the duration of this export, and then are discarded. + // + // TODO: generate some nice looking "+"/"-" labels here. + protected void GenerateAdditionalExtraLabels() + { + if (Settings.Unlabeled == FormatUnlabeled.ShowNone) + return; + + for (var pointer = 0; pointer < Data.GetRomSize(); pointer += GetLineByteLength(pointer)) + { + GenerateLabelIfNeededAt(pointer); + } + } + + private void GenerateLabelIfNeededAt(int pcoffset) + { + var snes = GetAddressOfAnyUsefulLabelsAt(pcoffset); + if (snes == -1) + return; + + var labelName = Data.GetDefaultLabel(snes); + AddExtraLabel(snes, new Label() { + Name = labelName, + }); + } + + protected int GetAddressOfAnyUsefulLabelsAt(int pcoffset) + { + if (Settings.Unlabeled == FormatUnlabeled.ShowAll) + return Data.ConvertPCtoSnes(pcoffset); // this may not be right either... + + var flag = Data.GetFlag(pcoffset); + var usefulToCreateLabelFrom = + flag == Data.FlagType.Opcode || flag == Data.FlagType.Pointer16Bit || + flag == Data.FlagType.Pointer24Bit || flag == Data.FlagType.Pointer32Bit; + + if (!usefulToCreateLabelFrom) + return -1; + + var snesIa = Data.GetIntermediateAddressOrPointer(pcoffset); + var pc = Data.ConvertSnesToPc(snesIa); + return pc >= 0 ? snesIa : -1; + } + + public static bool ValidateFormat(string formatString) + { + var tokens = formatString.ToLower().Split('%'); + + // not valid if format has an odd amount of %s + if (tokens.Length % 2 == 0) return false; + + for (int i = 1; i < tokens.Length; i += 2) + { + int indexOfColon = tokens[i].IndexOf(':'); + string kind = indexOfColon >= 0 ? tokens[i].Substring(0, indexOfColon) : tokens[i]; + + // not valid if base token isn't one we know of + if (!Parameters.ContainsKey(kind)) + return false; + + // not valid if parameter isn't an integer + int oof; + if (indexOfColon >= 0 && !int.TryParse(tokens[i].Substring(indexOfColon + 1), out oof)) + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Diz.Core/export/LogCreator.cs b/Diz.Core/export/LogCreator.cs new file mode 100644 index 00000000..be340c78 --- /dev/null +++ b/Diz.Core/export/LogCreator.cs @@ -0,0 +1,670 @@ +using System; +using System.Collections.Generic; +using Diz.Core.model; +using Diz.Core.util; +using IX.Observable; + +namespace Diz.Core.export +{ + public partial class LogCreator + { + public enum FormatUnlabeled + { + ShowAll = 0, + ShowInPoints = 1, // TODO Add Show In Points with +/- labels + ShowNone = 2 + } + + public enum FormatStructure + { + SingleFile = 0, + OneBankPerFile = 1 + } + + public LogWriterSettings Settings { get; set; } + public Data Data { get; set; } + + protected LogCreatorOutput Output; + protected Dictionary ExtraLabels { get; set; } = new Dictionary(); + protected List> ParseList; + protected List LabelsWeVisited; + protected int BankSize; + protected ObservableDictionary BackupOfOriginalLabelsBeforeModifying; + + public virtual OutputResult CreateLog() + { + OutputResult result; + + try + { + Init(); + WriteLog(); + result = GetResult(); + CloseOutput(result); + } + finally + { + Cleanup(); // critical to ALWAYS do this so we restore labels + } + + return result; + } + + protected virtual void Init() + { + InitOutput(); + SetupParseList(); + + BankSize = RomUtil.GetBankSize(Data.RomMapMode); + ErrorCount = 0; + LabelsWeVisited = new List(); + + GenerateAdditionalExtraLabels(); + WriteGeneratedLabelsIntoUnderlyingData(); // MODIFIES DATA. MAKE SURE TO UNDO THIS. + } + + private void InitOutput() + { + if (Settings.OutputToString) + Output = new LogCreatorStringOutput(); + else + Output = new LogCreatorStreamOutput(); + + Output.Init(this); + } + + protected virtual void Cleanup() + { + // restore original labels. SUPER IMPORTANT THIS HAPPENS WHen WE'RE DONE + // TODO: make this unnecessary by not modifying the underlying data. + RestoreUnderlyingDataLabels(); + } + + protected void RestoreUnderlyingDataLabels() + { + // SUPER IMPORTANT. THIS MUST GET DONE, ALWAYS. PROTECT THIS WITH TRY/CATCH + + if (BackupOfOriginalLabelsBeforeModifying != null) + Data.Labels = BackupOfOriginalLabelsBeforeModifying; + + BackupOfOriginalLabelsBeforeModifying = null; + } + + + private void CloseOutput(OutputResult result) + { + Output?.Finish(result); + Output = null; + } + + private OutputResult GetResult() + { + var result = new OutputResult() + { + ErrorCount = ErrorCount, + Success = true, + LogCreator = this + }; + + if (Settings.OutputToString) + result.OutputStr = ((LogCreatorStringOutput)Output)?.OutputString; + + return result; + } + + protected void WriteGeneratedLabelsIntoUnderlyingData() + { + // WARNING: THIS MODIFIES THE UNDERLYING DATA TO ADD MORE LABELS + // *** if not properly cleaned up, the original project file's label list can get trashed. *** + // always call this FN with a try/finally that cleans up after. + + // TODO: I really don't like us modifying and restoring the + // underlying labels or anything in Data. Data should ideally be immutable by us. + // we should either clone all of Data before modifying, or generate these labels on the fly. + BackupOfOriginalLabelsBeforeModifying = Data.Labels; + Data.Labels = new ObservableDictionary(Data.Labels); + + // write the new generated labels in, don't let them overwrite any real labels + // i.e. if the user defined a label like "PlayerSwimmingSprites", and our auto-generated + // labels also contain a label at the same address, then ignore our auto-generated label, + // only use the explicit user-created label. + foreach (var label in ExtraLabels) + { + Data.AddLabel(label.Key, label.Value, false); + } + } + + public int GetRomSize() + { + return Settings.RomSizeOverride != -1 ? Settings.RomSizeOverride : Data.GetRomSize(); + } + + protected virtual void WriteLog() + { + var size = GetRomSize(); + + WriteMainIncludes(size); + var pointer = WriteMainAssembly(size); + WriteLabels(pointer); + } + + private int WriteMainAssembly(int size) + { + // perf: this is the meat of the export, takes a while + var pointer = 0; + var bank = -1; + while (pointer < size) + { + WriteAddress(ref pointer, ref bank); + } + return pointer; + } + + protected void WriteSpecial(string special) + { + const int doesntMatter = 0; + var line = GetLine(doesntMatter, special); + Output.WriteLine(line); + } + + protected void WriteMainIncludes(int size) + { + WriteSpecial("map"); + WriteSpecial("empty"); + WriteMainBankIncludes(size); + } + + private void WriteMainBankIncludes(int size) + { + if (Settings.Structure != FormatStructure.OneBankPerFile) + return; + + for (var i = 0; i < size; i += BankSize) + Output.WriteLine(GetLine(i, "incsrc")); + + Output.WriteLine(GetLine(-1, "incsrc")); + } + + protected void SetupParseList() + { + var split = Settings.Format.Split('%'); + ParseList = new List>(); + for (var i = 0; i < split.Length; i++) + { + if (i % 2 == 0) ParseList.Add(Tuple.Create(split[i], int.MaxValue)); + else + { + var colon = split[i].IndexOf(':'); + + Tuple tuple; + + if (colon < 0) + { + var s1 = split[i]; + var s2 = Parameters[s1].Item2; + tuple = Tuple.Create(s1, s2); + } + else + { + tuple = Tuple.Create(split[i].Substring(0, colon), int.Parse(split[i].Substring(colon + 1))); + } + + ParseList.Add(tuple); + } + } + } + + protected void WriteAddress(ref int pointer, ref int currentBank) + { + SwitchBanksIfNeeded(pointer, ref currentBank); + + WriteBlankLineIfStartingNewParagraph(pointer); + WriteTheRealLine(pointer); + WriteBlankLineIfEndPoint(pointer); + + pointer += GetLineByteLength(pointer); + } + + private void WriteTheRealLine(int pointer) + { + Output.WriteLine(GetLine(pointer, null)); + } + + private void WriteBlankLineIfEndPoint(int pointer) + { + if ((Data.GetInOutPoint(pointer) & Data.InOutPoint.EndPoint) != 0) + Output.WriteLine(GetLine(pointer, "empty")); + } + + private void WriteBlankLineIfStartingNewParagraph(int pointer) + { + var isLocationAReadPoint = (Data.GetInOutPoint(pointer) & Data.InOutPoint.ReadPoint) != 0; + var anyLabelsPresent = Data.Labels.TryGetValue(pointer, out var label) && label.Name.Length > 0; + + if (isLocationAReadPoint || anyLabelsPresent) + Output.WriteLine(GetLine(pointer, "empty")); + } + + private void SwitchBanksIfNeeded(int pointer, ref int currentBank) + { + var snesAddress = Data.ConvertPCtoSnes(pointer); + + var thisBank = snesAddress >> 16; + + if (thisBank == currentBank) + return; + + OpenNewBank(pointer, thisBank); + currentBank = thisBank; + + if (snesAddress % BankSize == 0) + return; + + ReportError(pointer, "An instruction crossed a bank boundary."); + } + + private void OpenNewBank(int pointer, int thisBank) + { + Output.SwitchToBank(thisBank); + + Output.WriteLine(GetLine(pointer, "empty")); + Output.WriteLine(GetLine(pointer, "org")); + Output.WriteLine(GetLine(pointer, "empty")); + } + + protected void WriteLabels(int pointer) + { + var unvisitedLabels = GetUnvisitedLabels(); + WriteAnyUnivisitedLabels(pointer, unvisitedLabels); + PrintAllLabelsIfRequested(pointer, unvisitedLabels); + } + + private Dictionary GetUnvisitedLabels() + { + var unvisitedLabels = new Dictionary(); + + // part 1: important: include all labels we aren't defining somewhere else. needed for disassembly + foreach (var pair in Data.Labels) + { + if (LabelsWeVisited.Contains(pair.Key)) + continue; + + // this label was not defined elsewhere in our disassembly, so we need to include it in labels.asm + unvisitedLabels.Add(pair.Key, pair.Value); + } + + return unvisitedLabels; + } + + private void WriteAnyUnivisitedLabels(int pointer, Dictionary unvisitedLabels) + { + SwitchOutputStream(pointer, "labels"); + + foreach (var pair in unvisitedLabels) + Output.WriteLine(GetLine(pair.Key, "labelassign")); + } + + private void PrintAllLabelsIfRequested(int pointer, Dictionary unvisitedLabels) + { + // part 2: optional: if requested, print all labels regardless of use. + // Useful for debugging, documentation, or reverse engineering workflow. + // this file shouldn't need to be included in the build, it's just reference documentation + + if (!Settings.IncludeUnusedLabels) + return; + + SwitchOutputStream(pointer, "all-labels.txt"); // TODO: csv in the future. escape commas + foreach (var pair in Data.Labels) + { + // not the best place to add formatting, TODO: cleanup + var category = unvisitedLabels.ContainsKey(pair.Key) ? "UNUSED" : "USED"; + Output.WriteLine($";!^!-{category}-! " + GetLine(pair.Key, "labelassign")); + } + } + + protected void SwitchOutputStream(int pointer, string streamName) + { + Output.SwitchToStream(streamName); + + // write an extra blank line if we would normally switch files here + if (Settings.Structure == FormatStructure.SingleFile) + Output.WriteLine(GetLine(pointer, "empty")); + } + + // -------------------------- + #region WriteOperations + + protected string GetLine(int offset, string special) + { + var isSpecial = special != null; + var line = ""; + + foreach (var t in ParseList) + { + if (t.Item2 == int.MaxValue) // string literal + { + line += t.Item1; + } + else if (isSpecial) // special parameter (replaces code & everything else = empty) + { + var v1 = (t.Item1 == "code" ? special : "empty"); + line += GetParameter(offset, $"%{v1}", t.Item2); + } + else // normal parameter + { + line += GetParameter(offset, t.Item1, t.Item2); + } + } + + if (!isSpecial) + CheckForErrorsAtLine(offset); + + return line; + } + + private void CheckForErrorsAtLine(int offset) + { + // throw out some errors if stuff looks fishy + ErrorIfOperand(offset); + ErrorIfAdjacentOperandsSeemWrong(offset); + ErrorIfBranchToNonInstruction(offset); + } + + private Data.FlagType GetFlagButSwapOpcodeForOperand(int offset) + { + var flag = Data.GetFlag(offset); + if (flag == Data.FlagType.Opcode) + return Data.FlagType.Operand; + + return flag; + } + + protected int GetLineByteLength(int offset) + { + int max = 1, step = 1; + var size = Data.GetRomSize(); + + switch (Data.GetFlag(offset)) + { + case Data.FlagType.Opcode: + return Data.OpcodeByteLength(offset); + case Data.FlagType.Unreached: + case Data.FlagType.Operand: + case Data.FlagType.Data8Bit: + case Data.FlagType.Graphics: + case Data.FlagType.Music: + case Data.FlagType.Empty: + max = Settings.DataPerLine; + break; + case Data.FlagType.Text: + max = 21; + break; + case Data.FlagType.Data16Bit: + step = 2; + max = Settings.DataPerLine; + break; + case Data.FlagType.Data24Bit: + step = 3; + max = Settings.DataPerLine; + break; + case Data.FlagType.Data32Bit: + step = 4; + max = Settings.DataPerLine; + break; + case Data.FlagType.Pointer16Bit: + step = 2; + max = 2; + break; + case Data.FlagType.Pointer24Bit: + step = 3; + max = 3; + break; + case Data.FlagType.Pointer32Bit: + step = 4; + max = 4; + break; + } + + int min = step, myBank = offset / BankSize; + while ( + min < max && + offset + min < size && + Data.GetFlag(offset + min) == Data.GetFlag(offset) && + Data.GetLabelName(Data.ConvertPCtoSnes(offset + min)) == "" && + (offset + min) / BankSize == myBank + ) min += step; + return min; + } + + // just a % + [AssemblerHandler(Token = "", Length = 1)] + protected string GetPercent(int offset, int length) + { + return "%"; + } + + // all spaces + [AssemblerHandler(Token = "%empty", Length = 1)] + protected string GetEmpty(int offset, int length) + { + return string.Format("{0," + length + "}", ""); + } + + // trim to length + // negative length = right justified + [AssemblerHandler(Token = "label", Length = -22)] + protected string GetLabel(int offset, int length) + { + // what we're given: a PC offset in ROM. + // what we need to find: any labels (SNES addresses) that refer to it. + // + // i.e. given that we are at PC offset = 0, + // we find valid SNES offsets mirrored of 0xC08000 and 0x808000 which both refer to the same place + // + // TODO: we need to deal with that mirroring here + // TODO: eventually, support multiple labels tagging the same address, it may not always be just one. + + var snesOffset = Data.ConvertPCtoSnes(offset); + var label = Data.GetLabelName(snesOffset); + if (label == null) + return ""; + + LabelsWeVisited.Add(snesOffset); + + var noColon = label.Length == 0 || label[0] == '-' || label[0] == '+'; + return string.Format("{0," + (length * -1) + "}", label + (noColon ? "" : ":")); + } + + // trim to length + [AssemblerHandler(Token = "code", Length = 37)] + protected string GetCode(int offset, int length) + { + var bytes = GetLineByteLength(offset); + string code = ""; + + switch (Data.GetFlag(offset)) + { + case Data.FlagType.Opcode: + code = Data.GetInstruction(offset); + break; + case Data.FlagType.Unreached: + case Data.FlagType.Operand: + case Data.FlagType.Data8Bit: + case Data.FlagType.Graphics: + case Data.FlagType.Music: + case Data.FlagType.Empty: + code = Data.GetFormattedBytes(offset, 1, bytes); + break; + case Data.FlagType.Data16Bit: + code = Data.GetFormattedBytes(offset, 2, bytes); + break; + case Data.FlagType.Data24Bit: + code = Data.GetFormattedBytes(offset, 3, bytes); + break; + case Data.FlagType.Data32Bit: + code = Data.GetFormattedBytes(offset, 4, bytes); + break; + case Data.FlagType.Pointer16Bit: + code = Data.GetPointer(offset, 2); + break; + case Data.FlagType.Pointer24Bit: + code = Data.GetPointer(offset, 3); + break; + case Data.FlagType.Pointer32Bit: + code = Data.GetPointer(offset, 4); + break; + case Data.FlagType.Text: + code = Data.GetFormattedText(offset, bytes); + break; + } + + return string.Format("{0," + (length * -1) + "}", code); + } + + [AssemblerHandler(Token = "%org", Length = 37)] + protected string GetOrg(int offset, int length) + { + string org = "ORG " + Util.NumberToBaseString(Data.ConvertPCtoSnes(offset), Util.NumberBase.Hexadecimal, 6, true); + return string.Format("{0," + (length * -1) + "}", org); + } + + [AssemblerHandler(Token = "%map", Length = 37)] + protected string GetMap(int offset, int length) + { + string s = ""; + switch (Data.RomMapMode) + { + case RomMapMode.LoRom: s = "lorom"; break; + case RomMapMode.HiRom: s = "hirom"; break; + case RomMapMode.Sa1Rom: s = "sa1rom"; break; // todo + case RomMapMode.ExSa1Rom: s = "exsa1rom"; break; // todo + case RomMapMode.SuperFx: s = "sfxrom"; break; // todo + case RomMapMode.ExHiRom: s = "exhirom"; break; + case RomMapMode.ExLoRom: s = "exlorom"; break; + } + return string.Format("{0," + (length * -1) + "}", s); + } + + // 0+ = bank_xx.asm, -1 = labels.asm + [AssemblerHandler(Token = "%incsrc", Length = 1)] + protected string GetIncSrc(int offset, int length) + { + string s = "incsrc \"labels.asm\""; + if (offset >= 0) + { + int bank = Data.ConvertPCtoSnes(offset) >> 16; + s = string.Format("incsrc \"bank_{0}.asm\"", Util.NumberToBaseString(bank, Util.NumberBase.Hexadecimal, 2)); + } + return string.Format("{0," + (length * -1) + "}", s); + } + + [AssemblerHandler(Token = "%bankcross", Length = 1)] + protected string GetBankCross(int offset, int length) + { + string s = "check bankcross off"; + return string.Format("{0," + (length * -1) + "}", s); + } + + // length forced to 6 + [AssemblerHandler(Token = "ia", Length = 6)] + protected string GetIntermediateAddress(int offset, int length) + { + int ia = Data.GetIntermediateAddressOrPointer(offset); + return ia >= 0 ? Util.NumberToBaseString(ia, Util.NumberBase.Hexadecimal, 6) : " "; + } + + // length forced to 6 + [AssemblerHandler(Token = "pc", Length = 6)] + protected string GetProgramCounter(int offset, int length) + { + return Util.NumberToBaseString(Data.ConvertPCtoSnes(offset), Util.NumberBase.Hexadecimal, 6); + } + + // trim to length + [AssemblerHandler(Token = "offset", Length = -6)] + protected string GetOffset(int offset, int length) + { + return string.Format("{0," + (length * -1) + "}", Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 0)); + } + + // length forced to 8 + [AssemblerHandler(Token = "bytes", Length = 8)] + protected string GetRawBytes(int offset, int length) + { + string bytes = ""; + if (Data.GetFlag(offset) == Data.FlagType.Opcode) + { + for (var i = 0; i < Data.GetInstructionLength(offset); i++) + { + bytes += Util.NumberToBaseString(Data.GetRomByte(offset + i), Util.NumberBase.Hexadecimal); + } + } + return $"{bytes,-8}"; + } + + // trim to length + [AssemblerHandler(Token = "comment", Length = 1)] + protected string GetComment(int offset, int length) + { + var snesOffset = Data.ConvertPCtoSnes(offset); + return string.Format("{0," + (length * -1) + "}", Data.GetComment(snesOffset)); + } + + // length forced to 2 + [AssemblerHandler(Token = "b", Length = 2)] + protected string GetDataBank(int offset, int length) + { + return Util.NumberToBaseString(Data.GetDataBank(offset), Util.NumberBase.Hexadecimal, 2); + } + + // length forced to 4 + [AssemblerHandler(Token = "d", Length = 4)] + protected string GetDirectPage(int offset, int length) + { + return Util.NumberToBaseString(Data.GetDirectPage(offset), Util.NumberBase.Hexadecimal, 4); + } + + // if length == 1, M/m, else 08/16 + [AssemblerHandler(Token = "m", Length = 1)] + protected string GetMFlag(int offset, int length) + { + var m = Data.GetMFlag(offset); + if (length == 1) return m ? "M" : "m"; + else return m ? "08" : "16"; + } + + // if length == 1, X/x, else 08/16 + [AssemblerHandler(Token = "x", Length = 1)] + protected string GetXFlag(int offset, int length) + { + var x = Data.GetXFlag(offset); + if (length == 1) return x ? "X" : "x"; + else return x ? "08" : "16"; + } + + // output label at snes offset, and its value + [AssemblerHandler(Token = "%labelassign", Length = 1)] + protected string GetLabelAssign(int offset, int length) + { + var labelName = Data.GetLabelName(offset); + var offsetStr = Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 6, true); + var labelComment = Data.GetLabelComment(offset); + + if (string.IsNullOrEmpty(labelName)) + return ""; + + labelComment ??= ""; + + var finalCommentText = ""; + + // TODO: sorry, probably not the best way to stuff this in here, consider putting it in the %comment% section in the future. -Dom + if (Settings.PrintLabelSpecificComments && labelComment != "") + finalCommentText = $"; !^ {labelComment} ^!"; + + string s = $"{labelName} = {offsetStr}{finalCommentText}"; + return string.Format("{0," + (length * -1) + "}", s); + } + } + + #endregion +} diff --git a/Diz.Core/export/LogOutput.cs b/Diz.Core/export/LogOutput.cs new file mode 100644 index 00000000..76ae5c4e --- /dev/null +++ b/Diz.Core/export/LogOutput.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Diz.Core.util; + +namespace Diz.Core.export +{ + public abstract class LogCreatorOutput + { + protected LogCreator LogCreator; + + public void Init(LogCreator logCreator) + { + LogCreator = logCreator; + Init(); + } + + protected virtual void Init() { } + public virtual void Finish(LogCreator.OutputResult result) { } + public virtual void SwitchToBank(int bankNum) { } + public virtual void SwitchToStream(string streamName, bool isErrorStream = false) { } + public abstract void WriteLine(string line); + public abstract void WriteErrorLine(string line); + } + + public class LogCreatorStringOutput : LogCreatorOutput + { + protected StringBuilder OutputBuilder = new StringBuilder(); + protected StringBuilder ErrorBuilder = new StringBuilder(); + + public string OutputString => OutputBuilder.ToString(); + public string ErrorString => ErrorBuilder.ToString(); + + protected override void Init() + { + Debug.Assert(LogCreator.Settings.OutputToString && LogCreator.Settings.Structure == LogCreator.FormatStructure.SingleFile); + } + + public override void WriteLine(string line) => OutputBuilder.AppendLine(line); + public override void WriteErrorLine(string line) => ErrorBuilder.AppendLine(line); + + public override void Finish(LogCreator.OutputResult result) + { + result.OutputStr = OutputString; + } + } + + public class LogCreatorStreamOutput : LogCreatorOutput + { + protected Dictionary OutputStreams = new Dictionary(); + protected StreamWriter ErrorOutputStream; + + // references to stuff in outputStreams + private string activeStreamName; + private StreamWriter activeOutputStream; + + protected string Folder; + protected string Filename; // if set to single file output moe. + + protected override void Init() + { + var basePath = LogCreator.Settings.FileOrFolderOutPath; + + if (LogCreator.Settings.Structure == LogCreator.FormatStructure.OneBankPerFile) + basePath += "\\"; // force it to treat it as a path. not the best way. + + Folder = Path.GetDirectoryName(basePath); + + if (LogCreator.Settings.Structure == LogCreator.FormatStructure.SingleFile) + { + Filename = Path.GetFileName(LogCreator.Settings.FileOrFolderOutPath); + SwitchToStream(Filename); + } + else + { + SwitchToStream("main"); + } + + SwitchToStream(LogCreator.Settings.ErrorFilename, isErrorStream: true); + } + + public override void Finish(LogCreator.OutputResult result) + { + foreach (var stream in OutputStreams) + { + stream.Value.Close(); + } + OutputStreams.Clear(); + + activeOutputStream = null; + ErrorOutputStream = null; + activeStreamName = ""; + + if (result.ErrorCount == 0) + File.Delete(BuildStreamPath(LogCreator.Settings.ErrorFilename)); + } + + public override void SwitchToBank(int bankNum) + { + var bankStr = Util.NumberToBaseString(bankNum, Util.NumberBase.Hexadecimal, 2); + SwitchToStream($"bank_{ bankStr}"); + } + + public override void SwitchToStream(string streamName, bool isErrorStream = false) + { + // don't switch off the main file IF we're only supposed to be outputting one file + if (LogCreator.Settings.Structure == LogCreator.FormatStructure.SingleFile && + !string.IsNullOrEmpty(activeStreamName)) + return; + + var whichStream = OutputStreams.TryGetValue(streamName, out var outputStream) + ? outputStream + : OpenNewStream(streamName); + + if (!isErrorStream) + SetActiveStream(streamName, whichStream); + else + ErrorOutputStream = whichStream; + } + + public void SetActiveStream(string streamName, StreamWriter streamWriter) + { + activeStreamName = streamName; + activeOutputStream = streamWriter; + } + + protected virtual StreamWriter OpenNewStream(string streamName) + { + var streamWriter = new StreamWriter(BuildStreamPath(streamName)); + OutputStreams.Add(streamName, streamWriter); + return streamWriter; + } + + private string BuildStreamPath(string streamName) + { + var fullOutputPath = Path.Combine(Folder, streamName); + + if (!Path.HasExtension(fullOutputPath)) + fullOutputPath += ".asm"; + return fullOutputPath; + } + + public override void WriteLine(string line) + { + Debug.Assert(activeOutputStream != null && !string.IsNullOrEmpty(activeStreamName)); + activeOutputStream.WriteLine(line); + } + + public override void WriteErrorLine(string line) + { + ErrorOutputStream?.WriteLine(line); + } + } +} diff --git a/Diz.Core/export/LogWriterSettings.cs b/Diz.Core/export/LogWriterSettings.cs new file mode 100644 index 00000000..da8677dc --- /dev/null +++ b/Diz.Core/export/LogWriterSettings.cs @@ -0,0 +1,77 @@ +using System.IO; + +namespace Diz.Core.export +{ + public struct LogWriterSettings + { + // struct because we want to make a bunch of copies of this struct. + // The plumbing could use a pass of something like 'ref readonly' because: + // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref#reference-return-values + + public string Format; + public int DataPerLine; + public LogCreator.FormatUnlabeled Unlabeled; + public LogCreator.FormatStructure Structure; + public bool IncludeUnusedLabels; + public bool PrintLabelSpecificComments; + + public bool WasInitialized; + public int RomSizeOverride; // specify an override for the # of bytes to assemble. default is the entire ROM + + public string FileOrFolderOutPath; + public bool OutputToString; + public string ErrorFilename; + + public void SetDefaults() + { + Format = "%label:-22% %code:37%;%pc%|%bytes%|%ia%; %comment%"; + DataPerLine = 8; + Unlabeled = LogCreator.FormatUnlabeled.ShowInPoints; + Structure = LogCreator.FormatStructure.OneBankPerFile; + IncludeUnusedLabels = false; + PrintLabelSpecificComments = false; + FileOrFolderOutPath = ""; // path to output file or folder + WasInitialized = true; + RomSizeOverride = -1; + ErrorFilename = "errors.txt"; + } + + // return null if no error, or message if there is + public string Validate() + { + // for now, just make sure it was initialized somewhere by someone + if (!WasInitialized) + return "Not initialized"; + + // TODO: add more validation. + + if (OutputToString) + { + if (Structure == LogCreator.FormatStructure.OneBankPerFile) + return "Can't use one-bank-per-file output with string output"; + + if (FileOrFolderOutPath != "") + return "Can't use one-bank-per-file output with file output"; + } + else + { + if (FileOrFolderOutPath == "") + return "No file path set"; + + if (!Directory.Exists(Path.GetDirectoryName(FileOrFolderOutPath))) + return "File or folder output directory doesn't exist"; + + if (Structure == LogCreator.FormatStructure.SingleFile) + { + // don't check for existence, just that what we have appears to be a filename and + // not a directory. + if (Path.GetFileName(FileOrFolderOutPath) == string.Empty) + return "Output path doesn't appear to be a valid file selection"; + } + } + + return null; + } + } + +} diff --git a/Diz.Core/import/BSNESImportStreamProcessor.cs b/Diz.Core/import/BSNESImportStreamProcessor.cs new file mode 100644 index 00000000..fad35ec9 --- /dev/null +++ b/Diz.Core/import/BSNESImportStreamProcessor.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading; +using Diz.Core.util; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; + +namespace Diz.Core.import +{ + public class BsnesImportStreamProcessor + { + public struct CompressedWorkItems + { + public byte[] Bytes; + public int UncompressedSize; + } + + public struct WorkItem + { + public byte[] Buffer; + public bool AbridgedFormat; + } + + public CancellationTokenSource CancelToken { get; } = new CancellationTokenSource(); + + public IEnumerable GetCompressedWorkItems(Stream stream) + { + while (!CancelToken.IsCancellationRequested) + { + // perf: huge volume of data coming in. + // need to get the data read from the server as fast as possible. + // do minimal processing here then dump it over to the other thread for real processing. + yield return ReadPacketFromStream(stream); + } + } + + private static CompressedWorkItems ReadPacketFromStream(Stream stream) + { + const int headerSize = 9; + var buffer = Util.ReadNext(stream, headerSize, out _); + if (buffer.Length != headerSize) + { + throw new InvalidDataException($"invalid header length for compressed data chunk"); + } + + if (buffer[0] != 'Z') + { + throw new InvalidDataException($"expected header byte of 'Z', got {buffer[0]} instead."); + } + + var originalDataSizeBytes = ByteUtil.ByteArrayToInt32(buffer, 1); + var compressedDataSize = ByteUtil.ByteArrayToInt32(buffer, 5); + + buffer = Util.ReadNext(stream, compressedDataSize, out _); + if (buffer.Length != compressedDataSize) + { + throw new InvalidDataException( + $"compressed data: expected {compressedDataSize} bytes, only got {buffer.Length}"); + } + + return new CompressedWorkItems() + { + Bytes = buffer, + UncompressedSize = originalDataSizeBytes + }; + } + + public static IEnumerable ProcessCompressedWorkItems(CompressedWorkItems compressedWorkItems) + { + using var stream = new MemoryStream(DecompressWorkItems(compressedWorkItems)); + + foreach (var workItem in ReadWorkItems(stream)) + { + yield return workItem; + } + } + + private static byte[] DecompressWorkItems(CompressedWorkItems compressedWorkItems) + { + var decompressedData = new byte[compressedWorkItems.UncompressedSize]; + var decompressedLength = 0; + + using (var memory = new MemoryStream(compressedWorkItems.Bytes)) + { + using var inflater = new InflaterInputStream(memory); + decompressedLength = inflater.Read(decompressedData, 0, decompressedData.Length); + } + + if (decompressedLength != compressedWorkItems.UncompressedSize) + throw new InvalidDataException("incorrect decompressed data size"); + + return decompressedData; + } + + private static IEnumerable ReadWorkItems(Stream stream) + { + var header = new byte[2]; + while (stream.Read(header, 0, 2) == 2) + { + var id = header[0]; + var len = header[1]; + foreach (var workItem in ReadWorkItem(stream, id, len)) + { + yield return workItem; + } + } + } + + private static IEnumerable ReadWorkItem(Stream stream, byte workItemId, byte workItemLen) + { + if (workItemId != 0xEE && workItemId != 0xEF) + throw new InvalidDataException("Missing expected watermark from unzipped data"); + + var abridgedFormat = workItemId == 0xEE; + + var buffer = new byte[workItemLen]; + var bytesRead = stream.Read(buffer, 0, buffer.Length); + + if (bytesRead != buffer.Length) + throw new InvalidDataException("Didn't read enough bytes from unzipped data"); + + yield return new WorkItem() {Buffer = buffer, AbridgedFormat = abridgedFormat}; + } + } +} \ No newline at end of file diff --git a/Diz.Core/import/BSNESTraceLogCapture.cs b/Diz.Core/import/BSNESTraceLogCapture.cs new file mode 100644 index 00000000..28f41637 --- /dev/null +++ b/Diz.Core/import/BSNESTraceLogCapture.cs @@ -0,0 +1,206 @@ +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Diz.Core.model; +using Diz.Core.util; + +namespace Diz.Core.import +{ + // TODO: can probably replace this better with Dataflow TPL, investigate + + public class BsnesTraceLogCapture + { + private WorkerTaskManager taskManager; + private BsnesImportStreamProcessor streamProcessor; + + private readonly ReaderWriterLockSlim importerLock = new ReaderWriterLockSlim(); + private BsnesTraceLogImporter importer; + private int bytesToProcess = 0; + private int compressedBlocksToProcess = 0; + private BsnesTraceLogImporter.Stats cachedStats; + + public bool Running { get; protected set; } + + public int BytesToProcess => bytesToProcess; + public int BlocksToProcess => compressedBlocksToProcess; + public bool Finishing => streamProcessor?.CancelToken?.IsCancellationRequested ?? false; + + public void Run(Data data) + { + Setup(data); + try + { + Main(); + taskManager.WaitForAllTasksToComplete(); + } + finally + { + Shutdown(); + } + } + + private void Shutdown() + { + taskManager = null; + streamProcessor = null; + importer = null; + Running = false; + } + + private void Setup(Data data) + { + Running = true; + importer = new BsnesTraceLogImporter(data); + streamProcessor = new BsnesImportStreamProcessor(); + taskManager = new WorkerTaskManager(); + } + + private static NetworkStream Connect() + { + var tcpClient = new TcpClient(); + tcpClient.Connect(IPAddress.Loopback, 27015); + return tcpClient.GetStream(); + } + + private void Main() + { + var networkStream = Connect(); + ProcessStreamData(networkStream); + } + + private void ProcessStreamData(Stream networkStream) + { + using var enumerator = streamProcessor.GetCompressedWorkItems(networkStream).GetEnumerator(); + while (!streamProcessor.CancelToken.IsCancellationRequested && enumerator.MoveNext()) + { + var compressedItems = enumerator.Current; + taskManager.Run(() => ProcessCompressedWorkItem(compressedItems)); + States_MarkQueued(compressedItems); + } + } + + private async void ProcessCompressedWorkItem(BsnesImportStreamProcessor.CompressedWorkItems compressedItems) + { + // tune this as needed. + // we want parallel jobs going, but, we don't want too many of them at once. + // average # workItems per CompressedWorkItem is like 12K currently. + const int numItemsPerTask = 6000; + + using var enumerator = BsnesImportStreamProcessor.ProcessCompressedWorkItems(compressedItems).GetEnumerator(); + + bool keepGoing; + var itemsRemainingBeforeSend = numItemsPerTask; + var subTasks = new List(); + var workItemsForThisTask = new List(); + + do + { + var endOfList = !enumerator.MoveNext(); + keepGoing = !streamProcessor.CancelToken.IsCancellationRequested && !endOfList; + + if (!endOfList) + { + workItemsForThisTask.Add(enumerator.Current); + itemsRemainingBeforeSend--; + } + + var shouldSendNow = !keepGoing || itemsRemainingBeforeSend == 0; + if (!shouldSendNow) + continue; + + var workItemsCopy = new List(workItemsForThisTask); + subTasks.Add(taskManager.Run(() => + { + ProcessWorkItems(workItemsCopy); + })); + + itemsRemainingBeforeSend = numItemsPerTask; + workItemsForThisTask.Clear(); + } while (keepGoing); + + await Task.WhenAll(subTasks); + Stats_MarkCompleted(compressedItems); + } + + private void ProcessWorkItems(IEnumerable workItemsForThisTask) + { + foreach (var workItem in workItemsForThisTask) + { + ProcessWorkItem(workItem); + } + } + + // this is just neat stats. it's optional, remove if performance becomes an issue (seems unlikely) + private void States_MarkQueued(BsnesImportStreamProcessor.CompressedWorkItems compressedItems) + { + try + { + importerLock.EnterWriteLock(); + bytesToProcess += compressedItems.Bytes.Length; + compressedBlocksToProcess++; + } + finally + { + importerLock.ExitWriteLock(); + } + } + + private void Stats_MarkCompleted(BsnesImportStreamProcessor.CompressedWorkItems compressedItems) + { + try + { + importerLock.EnterWriteLock(); + bytesToProcess -= compressedItems.Bytes.Length; + compressedBlocksToProcess--; + } + finally + { + importerLock.ExitWriteLock(); + } + } + + private void ProcessWorkItem(BsnesImportStreamProcessor.WorkItem workItem) + { + try + { + // definitely hitting lock contention. + importerLock.EnterWriteLock(); + importer.ImportTraceLogLineBinary(workItem.Buffer, workItem.AbridgedFormat); + } + finally + { + importerLock.ExitWriteLock(); + } + } + + public void SignalToStop() + { + streamProcessor?.CancelToken?.Cancel(); + taskManager.StartFinishing(); + } + + public (BsnesTraceLogImporter.Stats stats, int bytesToProcess) GetStats() + { + if (importer == null) + return (cachedStats, 0); + + var cachedBytesInQueue = 0; + + try + { + importerLock.EnterReadLock(); + cachedStats = importer.CurrentStats; + cachedBytesInQueue = bytesToProcess; + } + finally + { + importerLock.ExitReadLock(); + } + + return (cachedStats, cachedBytesInQueue); + } + } +} \ No newline at end of file diff --git a/Diz.Core/import/BSNESTraceLogImporter.cs b/Diz.Core/import/BSNESTraceLogImporter.cs new file mode 100644 index 00000000..7be516e8 --- /dev/null +++ b/Diz.Core/import/BSNESTraceLogImporter.cs @@ -0,0 +1,300 @@ +using System; +using System.Diagnostics; +using System.IO; +using Diz.Core.model; +using Diz.Core.util; + +namespace Diz.Core.import +{ + public class BsnesTraceLogImporter + { + private Data data; + int romSize; + public struct Stats + { + public long NumRomBytesAnalyzed; + public long NumRomBytesModified; + + public long NumXFlagsModified; + public long NumMFlagsModified; + public long NumDbModified; + public long NumDpModified; + public long NumMarksModified; + } + + public Stats CurrentStats; + + public Data GetData() + { + return data; + } + + public BsnesTraceLogImporter(Data data) + { + this.data = data; + romSize = this.data.GetRomSize(); + + CurrentStats.NumRomBytesAnalyzed = 0; + CurrentStats.NumRomBytesModified = 0; + CurrentStats.NumXFlagsModified = 0; + CurrentStats.NumMFlagsModified = 0; + CurrentStats.NumDbModified = 0; + CurrentStats.NumDpModified = 0; + CurrentStats.NumMarksModified = 0; + } + + // this class exists for performance optimization ONLY. + // class representing offsets into a trace log + // we calculate it once from sample data and hang onto it + private class CachedTraceLineIndex + { + // NOTE: newer versions of BSNES use different text for flags. check for completeness. + private string sample = + "028cde rep #$30 A:0004 X:0000 Y:0004 S:1fdd D:0000 DB:02 nvmxdiZC V:133 H: 654 F:36"; + + // index of the start of the info + public readonly int + Addr, + D, + Db, + Flags, + FN, + FV, + FM, + FX, + FD, + FI, + FZ, + FC; + + public CachedTraceLineIndex() + { + int SkipToken(string token) + { + return sample.IndexOf(token) + token.Length; + } + + Addr = 0; + D = SkipToken("D:"); + Db = SkipToken("DB:"); + Flags = Db + 3; + + // flags: nvmxdizc + FN = Flags + 0; + FV = Flags + 1; + FM = Flags + 2; + FX = Flags + 3; + FD = Flags + 4; + FI = Flags + 5; + FZ = Flags + 6; + FC = Flags + 7; + } + } + + private static readonly CachedTraceLineIndex CachedIdx = new CachedTraceLineIndex(); + + public void ImportTraceLogLine(string line) + { + // caution: very performance-sensitive function, please take care when making modifications + // string.IndexOf() is super-slow too. + // Input lines must follow this strict format and be this exact formatting and column indices. + // 028cde rep #$30 A:0004 X:0000 Y:0004 S:1fdd D:0000 DB:02 nvmxdiZC V:133 H: 654 F:36 + + int GetHexValueAt(int startIndex, int length) + { + return Convert.ToInt32(line.Substring(startIndex, length), 16); + } + + if (line.Length < 80) + return; + + var snesAddress = GetHexValueAt(0, 6); + + // TODO: error treatment / validation + + var directPage = GetHexValueAt(CachedIdx.D, 4); + var dataBank = GetHexValueAt(CachedIdx.Db, 2); + + // 'X' = unchecked in bsnesplus debugger UI = (8bit), 'x' or '.' = checked (16bit) + var xflagSet = line[CachedIdx.FX] == 'X'; + + // 'M' = unchecked in bsnesplus debugger UI = (8bit), 'm' or '.' = checked (16bit) + var mflagSet = line[CachedIdx.FM] == 'M'; + + SetOpcodeAndOperandsFromTraceData(snesAddress, dataBank, directPage, xflagSet, mflagSet); + } + + // this is same as above but, reads the same data from a binary format. this is for + // performance reasons to try and stream the data live from BSNES + public void ImportTraceLogLineBinary(byte[] bytes, bool abridgedFormat=true) + { + // extremely performance-intensive function. be really careful when adding stuff + + if (abridgedFormat) { + Debug.Assert(bytes.Length == 8); + } else { + Debug.Assert(bytes.Length == 21); + } + + var pointer = 0; + + // ----------------------------- + var snesAddress = ByteUtil.ByteArrayToInt24(bytes, pointer); + pointer += 3; + + var opcodeLen = bytes[pointer++]; + + var directPage = ByteUtil.ByteArrayToInt16(bytes, pointer); + pointer += 2; + + var dataBank = bytes[pointer++]; + + // the flags register + var flags = bytes[pointer++]; + // n = flags & 0x80; + // v = flags & 0x40; + // m = flags & 0x20; + // d = flags & 0x08; + // i = flags & 0x04; + // z = flags & 0x02; + // c = flags & 0x01; + + // we only care about X and M flags + var xflagSet = (flags & 0x10) != 0; + var mflagSet = (flags & 0x20) != 0; + + if (!abridgedFormat) + { + // skip opcodes. NOTE: must read all 5 bytes but only use up to 'opcode_len' bytes + //var op = bytes[pointer++]; + //var op0 = bytes[pointer++]; + //var op1 = bytes[pointer++]; + //var op2 = bytes[pointer++]; + pointer += 4; + + // skip A register + pointer += 2; + + // skip X register + pointer += 2; + + // skip Y register + pointer += 2; + + // skip S register + pointer += 2; + + // skip, flag 'e' for emulation mode or not + // skip E(emu) flag <-- NOTE: we might... want this someday. + pointer += 1; + } + + Debug.Assert(pointer == bytes.Length); + + SetOpcodeAndOperandsFromTraceData(snesAddress, dataBank, directPage, xflagSet, mflagSet, opcodeLen); + } + + // Mark collected trace data for a RomByte (which should be an opcode) AND any of the operands that follow us. + private void SetOpcodeAndOperandsFromTraceData( + int snesAddress, int dataBank, int directPage, + bool xflagSet, bool mflagSet, + int opcodeLen = -1) + { + // extremely performance-intensive function. be really careful when adding stuff + var currentIndex = 0; + + while (true) + { + if (!SetOneRomByteFromTraceData(snesAddress, dataBank, directPage, xflagSet, mflagSet, opcodeLen, currentIndex)) + break; + + snesAddress = RomUtil.CalculateSnesOffsetWithWrap(snesAddress, 1); + currentIndex++; + } + } + + private bool SetOneRomByteFromTraceData(int snesAddress, int dataBank, int directPage, bool xflagSet, bool mflagSet, + int opcodeLen, int currentIndex) + { + CurrentStats.NumRomBytesAnalyzed++; + + var pc = data.ConvertSnesToPc(snesAddress); + if (!IsOkToSetThisRomByte(pc, currentIndex, opcodeLen)) + return false; + + var flagType = currentIndex == 0 ? Data.FlagType.Opcode : Data.FlagType.Operand; + + LogStats(pc, dataBank, directPage, xflagSet, mflagSet, flagType); + + // TODO: it doesn't hurt anything but, banks come back mirrored sometimes, + // would be cool to find a way to do the right thing and deal with the mirroring so we're not + // flipping and re-flipping bytes, but still maintain correctness with the game itself. + + // actually do the update + data.SetFlag(pc, flagType); + data.SetDataBank(pc, dataBank); + data.SetDirectPage(pc, directPage); + data.SetXFlag(pc, xflagSet); + data.SetMFlag(pc, mflagSet); + + return true; + } + + private bool IsOkToSetThisRomByte(int pc, int opIndex, int instructionByteLen) + { + if (pc < 0 || pc >= romSize) + return false; + + if (instructionByteLen != -1 && (instructionByteLen < 1 || instructionByteLen > 4)) + { + throw new InvalidDataException($"Inavalid opcode+operand byte length {instructionByteLen}. Must be -1, or between 1 and 4"); + } + if (opIndex < 0 || opIndex > 4) // yes, 4, not 3. it'll bail below + { + throw new InvalidDataException($"Inavalid opcode index {opIndex}. Must be between 0 and 4"); + } + + if (instructionByteLen != -1) + { + // just make sure we're in range if we know the amount of bytes we need to process + return opIndex < instructionByteLen; + } + else + { + // we don't know how many bytes to process, so have to do some fuzzy guessing now. play it safe. + + // easy: this is the first byte, this will be the Opcode, so clear that for takeoff. + if (opIndex == 0) + return true; + + // otherwise, this is NOT the first byte (Opcode), and we don't have information about how many bytes + // past us are Operands. Could be none, could be up to 3. + // + // We're trying to mark as many bytes as operands with the flags from the tracelog. + // We can't safely know though, so, unless they've ALREADY been marked as Operands, let's + // just play it safe and stop at the first thing that's NOT an Operand. + // + // Calling code should ideally not let us get to here, and instead supply us with a valid instructionByteLen + return data.GetFlag(pc) == Data.FlagType.Operand; + } + } + + private void LogStats(int pc, int dataBank, int directPage, bool xflagSet, bool mflagSet, Data.FlagType flagType) + { + var mMarks = data.GetFlag(pc) != flagType; + var mDb = data.GetDataBank(pc) != dataBank; + var mDp = data.GetDirectPage(pc) != directPage; + var mX = data.GetXFlag(pc) != xflagSet; + var mM = data.GetMFlag(pc) != mflagSet; + + if (mMarks || mDb || mDp || mX || mM) + CurrentStats.NumRomBytesModified++; + + CurrentStats.NumMarksModified += mMarks ? 1 : 0; + CurrentStats.NumDbModified += mDb ? 1 : 0; + CurrentStats.NumDpModified += mDp ? 1 : 0; + CurrentStats.NumXFlagsModified += mX ? 1 : 0; + CurrentStats.NumMFlagsModified += mM ? 1 : 0; + } + } +} \ No newline at end of file diff --git a/Diz.Core/import/BSNESUsageMapImporter.cs b/Diz.Core/import/BSNESUsageMapImporter.cs new file mode 100644 index 00000000..cf084152 --- /dev/null +++ b/Diz.Core/import/BSNESUsageMapImporter.cs @@ -0,0 +1,75 @@ +using System; +using Diz.Core.model; + +namespace Diz.Core.import +{ + public class BsnesUsageMapImporter + { + // TODO: move BsnesPlusUsage stuff to its own class outside of Data + [Flags] + public enum BsnesPlusUsage : byte + { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + UsageOpcode = 0x10, + UsageFlagM = 0x02, + UsageFlagX = 0x01, + }; + + // move out of here to extension method or just external method + public int ImportUsageMap(byte[] usageMap, Data data) + { + int size = data.GetRomSize(); + int modified = 0; + int prevFlags = 0; + + for (int map = 0; map <= 0xFFFFFF; map++) + { + var i = data.ConvertSnesToPc(map); + + if (i == -1 || i >= size) + { + // branch predictor may optimize this + continue; + } + + var flags = (BsnesPlusUsage) usageMap[map]; + + if (flags == 0) + { + // no information available + continue; + } + + if (data.GetFlag(i) != Data.FlagType.Unreached) + { + // skip if there is something already set.. + continue; + } + + // opcode: 0x30, operand: 0x20 + if (flags.HasFlag(BsnesPlusUsage.UsageExec)) + { + data.SetFlag(i, Data.FlagType.Operand); + + if (flags.HasFlag(BsnesPlusUsage.UsageOpcode)) + { + prevFlags = ((int) flags & 3) << 4; + data.SetFlag(i, Data.FlagType.Opcode); + } + + data.SetMxFlags(i, prevFlags); + modified++; + } + else if (flags.HasFlag(BsnesPlusUsage.UsageRead)) + { + data.SetFlag(i, Data.FlagType.Data8Bit); + modified++; + } + } + + return modified; + } + } +} diff --git a/Diz.Core/import/BizHawkCdlImporter.cs b/Diz.Core/import/BizHawkCdlImporter.cs new file mode 100644 index 00000000..091d6a26 --- /dev/null +++ b/Diz.Core/import/BizHawkCdlImporter.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Diz.Core.model; + +namespace Diz.Core.import +{ + public class BizHawkCdlImporter + { + private readonly Dictionary> cdl = new Dictionary>(); + + [Flags] + public enum Flag : byte + { + None = 0x00, + ExecFirst = 0x01, + ExecOperand = 0x02, + CpuData = 0x04, + DmaData = 0x08, + CpuxFlag = 0x10, + CpumFlag = 0x20, + Brr = 0x80 + } + + public static void Import(string filename, Data data) + { + var cdl = new BizHawkCdlImporter(); + cdl.LoadFromFile(filename); + cdl.CopyInto(data); + } + + private void LoadFromFile(string path) + { + using var fs = new FileStream(path, FileMode.Open); + LoadFromStream(fs); + } + + private void LoadFromStream(Stream input) + { + var br = new BinaryReader(input); + + string id = br.ReadString(); + string subType = id switch + { + "BIZHAWK-CDL-1" => "PCE", + "BIZHAWK-CDL-2" => br.ReadString().TrimEnd(' '), + _ => throw new InvalidDataException("File is not a BizHawk CDL file.") + }; + + if (subType != "SNES") + { + throw new InvalidDataException("The CDL file is not for SNES."); + } + + int count = br.ReadInt32(); + for (var i = 0; i < count; i++) + { + string key = br.ReadString(); + int len = br.ReadInt32(); + var data = br.ReadBytes(len).Select(b => (Flag)b).ToArray(); + cdl[key] = data; + } + } + private void CopyInto(Data data) + { + if (!cdl.TryGetValue("CARTROM", out var cdlRomFlags)) + { + throw new InvalidDataException("The CDL file does not contain CARTROM block."); + } + + var size = Math.Min(cdlRomFlags.Count, data.GetRomSize()); + bool m = false; + bool x = false; + for (var offset = 0; offset < size; offset++) + { + var cdlFlag = cdlRomFlags[offset]; + if (cdlFlag == BizHawkCdlImporter.Flag.None) + continue; + + var type = Data.FlagType.Unreached; + if ((cdlFlag & BizHawkCdlImporter.Flag.ExecFirst) != 0) + { + type = Data.FlagType.Opcode; + m = (cdlFlag & BizHawkCdlImporter.Flag.CpumFlag) != 0; + x = (cdlFlag & BizHawkCdlImporter.Flag.CpuxFlag) != 0; + } + else if ((cdlFlag & BizHawkCdlImporter.Flag.ExecOperand) != 0) + type = Data.FlagType.Operand; + else if ((cdlFlag & BizHawkCdlImporter.Flag.CpuData) != 0) + type = Data.FlagType.Data8Bit; + else if ((cdlFlag & BizHawkCdlImporter.Flag.DmaData) != 0) + type = Data.FlagType.Data8Bit; + data.MarkTypeFlag(offset, type, 1); + + if (type != Data.FlagType.Opcode && type != Data.FlagType.Operand) + continue; + + // Operand reuses the last M and X flag values used in Opcode, + // since BizHawk CDL records M and X flags only in Opcode. + data.MarkMFlag(offset, m, 1); + data.MarkXFlag(offset, x, 1); + } + } + } +} diff --git a/Diz.Core/model/Data.Constants.cs b/Diz.Core/model/Data.Constants.cs new file mode 100644 index 00000000..ad112d79 --- /dev/null +++ b/Diz.Core/model/Data.Constants.cs @@ -0,0 +1,84 @@ +using System; +using System.ComponentModel; +using System.Drawing; + +namespace Diz.Core.model +{ + public partial class Data + { + [System.AttributeUsage(System.AttributeTargets.All)] + public class ColorDescriptionAttribute : System.Attribute + { + public KnownColor Color { get; } + + public ColorDescriptionAttribute(KnownColor c) + { + Color = c; + } + } + + public enum FlagType : byte + { + [ColorDescription(KnownColor.Black)] Unreached = 0x00, + + [ColorDescription(KnownColor.Yellow)] Opcode = 0x10, + + [ColorDescription(KnownColor.YellowGreen)] + Operand = 0x11, + + [ColorDescription(KnownColor.NavajoWhite)] [Description("Data (8-bit)")] + Data8Bit = 0x20, + + [ColorDescription(KnownColor.LightPink)] + Graphics = 0x21, + + [ColorDescription(KnownColor.PowderBlue)] + Music = 0x22, + + [ColorDescription(KnownColor.DarkSlateGray)] + Empty = 0x23, + + [ColorDescription(KnownColor.NavajoWhite)] [Description("Data (16-bit)")] + Data16Bit = 0x30, + + [ColorDescription(KnownColor.Orchid)] [Description("Pointer (16-bit)")] + Pointer16Bit = 0x31, + + [ColorDescription(KnownColor.NavajoWhite)] [Description("Data (24-bit)")] + Data24Bit = 0x40, + + [ColorDescription(KnownColor.Orchid)] [Description("Pointer (24-bit)")] + Pointer24Bit = 0x41, + + [ColorDescription(KnownColor.NavajoWhite)] [Description("Data (32-bit)")] + Data32Bit = 0x50, + + [ColorDescription(KnownColor.Orchid)] [Description("Pointer (32-bit)")] + Pointer32Bit = 0x51, + + [ColorDescription(KnownColor.Aquamarine)] + Text = 0x60 + } + + public enum Architecture : byte + { + [Description("65C816")] Cpu65C816 = 0x00, + [Description("SPC700")] Apuspc700 = 0x01, + [Description("SuperFX")] GpuSuperFx = 0x02 + } + + [Flags] + public enum InOutPoint : byte + { + InPoint = 0x01, + OutPoint = 0x02, + EndPoint = 0x04, + ReadPoint = 0x08 + } + + public const int LoromSettingOffset = 0x7FD5; + public const int HiromSettingOffset = 0xFFD5; + public const int ExhiromSettingOffset = 0x40FFD5; + public const int ExloromSettingOffset = 0x407FD5; + } +} \ No newline at end of file diff --git a/Diz.Core/model/Data.Properties.cs b/Diz.Core/model/Data.Properties.cs new file mode 100644 index 00000000..bcb88527 --- /dev/null +++ b/Diz.Core/model/Data.Properties.cs @@ -0,0 +1,80 @@ +using System.Linq; +using Diz.Core.util; +using IX.Observable; + +namespace Diz.Core.model +{ + public partial class Data + { + // don't modify these directly, always go through the public properties so + // other objects can subscribe to modification notifications + private RomMapMode romMapMode; + private RomSpeed romSpeed = RomSpeed.Unknown; + private ObservableDictionary comments = new ObservableDictionary(); + private ObservableDictionary labels = new ObservableDictionary(); + private RomBytes romBytes = new RomBytes(); + + // Note: order of these public properties matters for the load/save process. Keep 'RomBytes' LAST + // TODO: should be a way in the XML serializer to control the order, remove this comment + // when we figure it out. + public RomMapMode RomMapMode + { + get => romMapMode; + set => SetField(ref romMapMode, value); + } + + public RomSpeed RomSpeed + { + get => romSpeed; + set => SetField(ref romSpeed, value); + } + + // next 2 dictionaries store in SNES address format (since memory labels can't be represented as a PC address) + public ObservableDictionary Comments + { + get => comments; + set => SetField(ref comments, value); + } + + public ObservableDictionary Labels + { + get => labels; + set => SetField(ref labels, value); + } + + // RomBytes stored as PC file offset addresses (since ROM will always be mapped to disk) + public RomBytes RomBytes + { + get => romBytes; + set => SetField(ref romBytes, value); + } + + + #region Equality + protected bool Equals(Data other) + { + return Labels.SequenceEqual(other.Labels) && RomMapMode == other.RomMapMode && RomSpeed == other.RomSpeed && Comments.SequenceEqual(other.Comments) && RomBytes.Equals(other.RomBytes); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == this.GetType() && Equals((Data)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Labels.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)RomMapMode; + hashCode = (hashCode * 397) ^ (int)RomSpeed; + hashCode = (hashCode * 397) ^ Comments.GetHashCode(); + hashCode = (hashCode * 397) ^ RomBytes.GetHashCode(); + return hashCode; + } + } + #endregion + } +} \ No newline at end of file diff --git a/Diz.Core/model/Data.cs b/Diz.Core/model/Data.cs new file mode 100644 index 00000000..73f9b6db --- /dev/null +++ b/Diz.Core/model/Data.cs @@ -0,0 +1,551 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Diz.Core.arch; +using Diz.Core.util; +using DiztinGUIsh; + +namespace Diz.Core.model +{ + public partial class Data : DizDataModel + { + // TODO: this really shouldn't be in Data, move to an outside 'SNESSystem' class or something that operates on Data + private readonly Cpu65C816 cpu65C816; + + public Data() + { + cpu65C816 = new Cpu65C816(this); + } + + public void CreateRomBytesFromRom(IEnumerable actualRomBytes) + { + Debug.Assert(RomBytes.Count == 0); + + var previousNotificationState = RomBytes.SendNotificationChangedEvents; + RomBytes.SendNotificationChangedEvents = false; + + RomBytes.Clear(); + foreach (var fileByte in actualRomBytes) + { + RomBytes.Add(new RomByte + { + Rom = fileByte, + }); + } + + RomBytes.SendNotificationChangedEvents = previousNotificationState; + } + + private byte[] GetRomBytes(int pcOffset, int count) + { + var output = new byte[count]; + for (var i = 0; i < output.Length; i++) + output[i] = (byte)GetRomByte(ConvertSnesToPc(pcOffset + i)); + + return output; + } + + public string GetRomNameFromRomBytes() + { + return Encoding.UTF8.GetString(GetRomBytes(0xFFC0, 21)); + } + + public int GetRomCheckSumsFromRomBytes() + { + return ByteUtil.ByteArrayToInt32(GetRomBytes(0xFFDC, 4)); + } + + public void CopyRomDataIn(IEnumerable trueRomBytes) + { + var previousNotificationState = RomBytes.SendNotificationChangedEvents; + RomBytes.SendNotificationChangedEvents = false; + + var i = 0; + foreach (var b in trueRomBytes) + { + RomBytes[i].Rom = b; + ++i; + } + Debug.Assert(RomBytes.Count == i); + + RomBytes.SendNotificationChangedEvents = previousNotificationState; + } + + public int GetRomSize() => RomBytes?.Count ?? 0; + public FlagType GetFlag(int i) => RomBytes[i].TypeFlag; + public void SetFlag(int i, FlagType flag) => RomBytes[i].TypeFlag = flag; + public Architecture GetArchitecture(int i) => RomBytes[i].Arch; + public void SetArchitecture(int i, Architecture arch) => RomBytes[i].Arch = arch; + public InOutPoint GetInOutPoint(int i) => RomBytes[i].Point; + public void SetInOutPoint(int i, InOutPoint point) => RomBytes[i].Point |= point; + public void ClearInOutPoint(int i) => RomBytes[i].Point = 0; + public int GetDataBank(int i) => RomBytes[i].DataBank; + public void SetDataBank(int i, int dBank) => RomBytes[i].DataBank = (byte)dBank; + public int GetDirectPage(int i) => RomBytes[i].DirectPage; + public void SetDirectPage(int i, int dPage) => RomBytes[i].DirectPage = 0xFFFF & dPage; + public bool GetXFlag(int i) => RomBytes[i].XFlag; + public void SetXFlag(int i, bool x) => RomBytes[i].XFlag = x; + public bool GetMFlag(int i) => RomBytes[i].MFlag; + public void SetMFlag(int i, bool m) => RomBytes[i].MFlag = m; + public int GetMxFlags(int i) + { + return (RomBytes[i].MFlag ? 0x20 : 0) | (RomBytes[i].XFlag ? 0x10 : 0); + } + public void SetMxFlags(int i, int mx) + { + RomBytes[i].MFlag = ((mx & 0x20) != 0); + RomBytes[i].XFlag = ((mx & 0x10) != 0); + } + public string GetLabelName(int i) + { + if (Labels.TryGetValue(i, out var val)) + return val?.Name ?? ""; + + return ""; + } + public string GetLabelComment(int i) + { + if (Labels.TryGetValue(i, out var val)) + return val?.Comment ?? ""; + + return ""; + } + + public void DeleteAllLabels() + { + Labels.Clear(); + } + + public void AddLabel(int offset, Label label, bool overwrite) + { + // adding null label removes it + if (label == null) + { + if (Labels.ContainsKey(offset)) + Labels.Remove(offset); + + return; + } + + if (overwrite) + { + if (Labels.ContainsKey(offset)) + Labels.Remove(offset); + } + + if (!Labels.ContainsKey(offset)) + { + label.CleanUp(); + Labels.Add(offset, label); + } + } + + public string GetComment(int i) => Comments.TryGetValue(i, out var val) ? val : ""; + public void AddComment(int i, string v, bool overwrite) + { + if (v == null) + { + if (Comments.ContainsKey(i)) Comments.Remove(i); + } else + { + if (Comments.ContainsKey(i) && overwrite) Comments.Remove(i); + if (!Comments.ContainsKey(i)) Comments.Add(i, v); + } + } + + public int ConvertPCtoSnes(int offset) + { + return RomUtil.ConvertPCtoSnes(offset, RomMapMode, RomSpeed); + } + public int GetRomByte(int i) => RomBytes[i].Rom; + public int GetRomWord(int offset) + { + if (offset + 1 < GetRomSize()) + return GetRomByte(offset) + (GetRomByte(offset + 1) << 8); + return -1; + } + public int GetRomLong(int offset) + { + if (offset + 2 < GetRomSize()) + return GetRomByte(offset) + (GetRomByte(offset + 1) << 8) + (GetRomByte(offset + 2) << 16); + return -1; + } + public int GetRomDoubleWord(int offset) + { + if (offset + 3 < GetRomSize()) + return GetRomByte(offset) + (GetRomByte(offset + 1) << 8) + (GetRomByte(offset + 2) << 16) + (GetRomByte(offset + 3) << 24); + return -1; + } + public int GetIntermediateAddressOrPointer(int offset) + { + switch (GetFlag(offset)) + { + case FlagType.Unreached: + case FlagType.Opcode: + return GetIntermediateAddress(offset, true); + case FlagType.Pointer16Bit: + int bank = GetDataBank(offset); + return (bank << 16) | GetRomWord(offset); + case FlagType.Pointer24Bit: + case FlagType.Pointer32Bit: + return GetRomLong(offset); + } + return -1; + } + + public int GetBankSize() + { + return RomUtil.GetBankSize(RomMapMode); + } + + public int OpcodeByteLength(int offset) + { + return GetArchitecture(offset) switch + { + Architecture.Cpu65C816 => cpu65C816.GetInstructionLength(offset), + Architecture.Apuspc700 => 1, + Architecture.GpuSuperFx => 1, + _ => 1 + }; + } + + private int UnmirroredOffset(int offset) + { + return RomUtil.UnmirroredOffset(offset, GetRomSize()); + } + + public string GetFormattedBytes(int offset, int step, int bytes) + { + var res = step switch + { + 1 => "db ", + 2 => "dw ", + 3 => "dl ", + 4 => "dd ", + _ => "" + }; + + for (var i = 0; i < bytes; i += step) + { + if (i > 0) res += ","; + + switch (step) + { + case 1: res += Util.NumberToBaseString(GetRomByte(offset + i), Util.NumberBase.Hexadecimal, 2, true); break; + case 2: res += Util.NumberToBaseString(GetRomWord(offset + i), Util.NumberBase.Hexadecimal, 4, true); break; + case 3: res += Util.NumberToBaseString(GetRomLong(offset + i), Util.NumberBase.Hexadecimal, 6, true); break; + case 4: res += Util.NumberToBaseString(GetRomDoubleWord(offset + i), Util.NumberBase.Hexadecimal, 8, true); break; + } + } + + return res; + } + + public int ConvertSnesToPc(int address) + { + return RomUtil.ConvertSnesToPc(address, RomMapMode, GetRomSize()); + } + + public string GetPointer(int offset, int bytes) + { + var ia = -1; + string format = "", param = ""; + switch (bytes) + { + case 2: + ia = (GetDataBank(offset) << 16) | GetRomWord(offset); + format = "dw {0}"; + param = Util.NumberToBaseString(GetRomWord(offset), Util.NumberBase.Hexadecimal, 4, true); + break; + case 3: + ia = GetRomLong(offset); + format = "dl {0}"; + param = Util.NumberToBaseString(GetRomLong(offset), Util.NumberBase.Hexadecimal, 6, true); + break; + case 4: + ia = GetRomLong(offset); + format = "dl {0}" + + $" : db {Util.NumberToBaseString(GetRomByte(offset + 3), Util.NumberBase.Hexadecimal, 2, true)}"; + param = Util.NumberToBaseString(GetRomLong(offset), Util.NumberBase.Hexadecimal, 6, true); + break; + } + + var pc = ConvertSnesToPc(ia); + if (pc >= 0 && GetLabelName(ia) != "") param = GetLabelName(ia); + return string.Format(format, param); + } + + public string GetFormattedText(int offset, int bytes) + { + var text = "db \""; + for (var i = 0; i < bytes; i++) text += (char)GetRomByte(offset + i); + return text + "\""; + } + + public string GetDefaultLabel(int snes) + { + var pcoffset = ConvertSnesToPc(snes); + var prefix = RomUtil.TypeToLabel(GetFlag(pcoffset)); + var labelAddress = Util.NumberToBaseString(snes, Util.NumberBase.Hexadecimal, 6); + return $"{prefix}_{labelAddress}"; + } + + public int Step(int offset, bool branch, bool force, int prevOffset) + { + return GetArchitecture(offset) switch + { + Architecture.Cpu65C816 => cpu65C816.Step(offset, branch, force, prevOffset), + Architecture.Apuspc700 => offset, + Architecture.GpuSuperFx => offset, + _ => offset + }; + } + + public int AutoStep(int offset, bool harsh, int amount) + { + int newOffset = offset, prevOffset = offset - 1, nextOffset; + if (harsh) + { + while (newOffset < offset + amount) + { + nextOffset = Step(newOffset, false, true, prevOffset); + prevOffset = newOffset; + newOffset = nextOffset; + } + } + else + { + var stack = new Stack(); + var seenBranches = new List(); + var keepGoing = true; + + while (keepGoing) + { + switch (GetArchitecture(newOffset)) + { + case Architecture.Cpu65C816: + if (seenBranches.Contains(newOffset)) + { + keepGoing = false; + break; + } + + var opcode = GetRomByte(newOffset); + + nextOffset = Step(newOffset, false, false, prevOffset); + var jumpOffset = Step(newOffset, true, false, prevOffset); + + if (opcode == 0x40 || opcode == 0xCB || opcode == 0xDB || opcode == 0xF8 // RTI WAI STP SED + || opcode == 0xFB || opcode == 0x00 || opcode == 0x02 || opcode == 0x42 // XCE BRK COP WDM + || opcode == 0x6C || opcode == 0x7C || opcode == 0xDC || opcode == 0xFC // JMP JMP JML JSR + ) keepGoing = false; + + if (opcode == 0x4C || opcode == 0x5C || opcode == 0x80 || opcode == 0x82 // JMP JML BRA BRL + || opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 // BPL BMI BVC BVS + || opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0 // BCC BCS BNE BEQ + ) seenBranches.Add(newOffset); + + if (opcode == 0x08) // PHP + { + stack.Push(GetMxFlags(newOffset)); + } + else if (opcode == 0x28) // PLP + { + if (stack.Count == 0) + { + keepGoing = false; break; + } + else + { + SetMxFlags(newOffset, stack.Pop()); + } + } + + if (opcode == 0x60 || opcode == 0x6B) // RTS RTL + { + if (stack.Count == 0) + { + keepGoing = false; + break; + } + else + { + prevOffset = newOffset; + newOffset = stack.Pop(); + } + } + else if (opcode == 0x20 || opcode == 0x22) // JSR JSL + { + stack.Push(nextOffset); + prevOffset = newOffset; + newOffset = jumpOffset; + } + else + { + prevOffset = newOffset; + newOffset = nextOffset; + } + break; + case Architecture.Apuspc700: + case Architecture.GpuSuperFx: + nextOffset = Step(newOffset, false, true, prevOffset); + prevOffset = newOffset; + newOffset = nextOffset; + break; + } + + var flag = GetFlag(newOffset); + if (!(flag == FlagType.Unreached || flag == FlagType.Opcode || flag == FlagType.Operand)) keepGoing = false; + } + } + return newOffset; + } + + public int Mark(Action MarkAction, int offset, int count) + { + int i, size = GetRomSize(); + for (i = 0; i < count && offset + i < size; i++) + MarkAction(offset + i); + + return offset + i < size ? offset + i : size - 1; + } + + public int MarkTypeFlag(int offset, FlagType type, int count) => Mark(i => SetFlag(i, type), offset, count); + public int MarkDataBank(int offset, int db, int count) => Mark(i => SetDataBank(i, db), offset, count); + public int MarkDirectPage(int offset, int dp, int count) => Mark(i => SetDirectPage(i, dp), offset, count); + public int MarkXFlag(int offset, bool x, int count) => Mark(i => SetXFlag(i, x), offset, count); + public int MarkMFlag(int offset, bool m, int count) => Mark(i => SetMFlag(i, m), offset, count); + public int MarkArchitecture(int offset, Architecture arch, int count) => Mark(i => SetArchitecture(i, arch), offset, count); + + public int GetInstructionLength(int offset) + { + return GetArchitecture(offset) switch + { + Architecture.Cpu65C816 => cpu65C816.GetInstructionLength(offset), + Architecture.Apuspc700 => 1, + Architecture.GpuSuperFx => 1, + _ => 1 + }; + } + + public int FixMisalignedFlags() + { + int count = 0, size = GetRomSize(); + + for (var i = 0; i < size; i++) + { + var flag = GetFlag(i); + + switch (flag) + { + case FlagType.Opcode: + { + int len = GetInstructionLength(i); + for (var j = 1; j < len && i + j < size; j++) + { + if (GetFlag(i + j) != FlagType.Operand) + { + SetFlag(i + j, FlagType.Operand); + count++; + } + } + i += len - 1; + break; + } + case FlagType.Operand: + SetFlag(i, FlagType.Opcode); + count++; + i--; + break; + default: + { + if (RomUtil.GetByteLengthForFlag(flag) > 1) + { + int step = RomUtil.GetByteLengthForFlag(flag); + for (int j = 1; j < step; j++) + { + if (GetFlag(i + j) == flag) + continue; + SetFlag(i + j, flag); + count++; + } + i += step - 1; + } + + break; + } + } + } + + return count; + } + + public void RescanInOutPoints() + { + for (var i = 0; i < GetRomSize(); i++) ClearInOutPoint(i); + + for (var i = 0; i < GetRomSize(); i++) + { + if (GetFlag(i) == FlagType.Opcode) + { + switch (GetArchitecture(i)) + { + case Architecture.Cpu65C816: cpu65C816.MarkInOutPoints(i); break; + case Architecture.Apuspc700: break; + case Architecture.GpuSuperFx: break; + } + } + } + } + + public int GetIntermediateAddress(int offset, bool resolve = false) + { + // FIX ME: log and generation of dp opcodes. search references + return GetArchitecture(offset) switch + { + Architecture.Cpu65C816 => cpu65C816.GetIntermediateAddress(offset, resolve), + Architecture.Apuspc700 => -1, + Architecture.GpuSuperFx => -1, + _ => -1 + }; + } + + public string GetInstruction(int offset) + { + return GetArchitecture(offset) switch + { + Architecture.Cpu65C816 => cpu65C816.GetInstruction(offset), + Architecture.Apuspc700 => "", + Architecture.GpuSuperFx => "", + _ => "" + }; + } + + public int GetNumberOfBanks() + { + return RomBytes.Count / GetBankSize(); + } + + public string GetBankName(int bankIndex) + { + var bankSnesByte = GetSnesBankByte(bankIndex); + return Util.NumberToBaseString(bankSnesByte, Util.NumberBase.Hexadecimal, 2); + } + + private int GetSnesBankByte(int bankIndex) + { + var bankStartingPcOffset = bankIndex << 16; + var bankSnesNumber = ConvertPCtoSnes(bankStartingPcOffset) >> 16; + return bankSnesNumber; + } + + // get the actual ROM file bytes (i.e. the contents of the SMC file on the disk) + // note: don't save these anywhere permanent because ROM data is usually copyrighted. + public IEnumerable GetFileBytes() + { + return RomBytes.Select(b => b.Rom); + } + } +} diff --git a/Diz.Core/model/Label.cs b/Diz.Core/model/Label.cs new file mode 100644 index 00000000..a51d3fd6 --- /dev/null +++ b/Diz.Core/model/Label.cs @@ -0,0 +1,49 @@ +namespace Diz.Core.model +{ + // represent a label at a particular address + // NOTE: you can have labels at addresses in: + // 1) ROM (they will show up in the main table view) + // 2) anything else, like RAM (they will only show up in the label editor, not in the main table) + // + // Comments here are for the LABEL itself, and not so much about where they're used. + // i.e. a label for 0x7E0020 might store a character's HP in RAM. It would look like: + // - address: 0x7E0020 (0x7EXXXX means it's a RAM address) + // - label: "character_3_hp" + // - comment: "this address is only used in RAM during battle sequences" + // ^^^^---- will not show up in the main table, just the editor + + public class Label + { + public string Name = ""; // name of the label + public string Comment = ""; // user-generated text, comment only + public void CleanUp() + { + Comment ??= ""; + Name ??= ""; + } + + #region Equality + + protected bool Equals(Label other) + { + return Name == other.Name && Comment == other.Comment; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Label)obj); + } + public override int GetHashCode() + { + unchecked + { + return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ (Comment != null ? Comment.GetHashCode() : 0); + } + } + + #endregion + } +} diff --git a/Diz.Core/model/Model.cs b/Diz.Core/model/Model.cs new file mode 100644 index 00000000..b385d606 --- /dev/null +++ b/Diz.Core/model/Model.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace DiztinGUIsh +{ + public class DizDataModel : PropertyNotifyChanged + { + + } + public class PropertyNotifyChanged : INotifyPropertyChanged + { + // this stuff lets other parts of code subscribe to events that get fired anytime + // properties of our class change. + // + // Just hook up SetField() to the 'set' param of any property you would like to + // expose to outside classes. + public event PropertyChangedEventHandler PropertyChanged; + protected bool SetField(ref T field, T value, bool compareRefOnly = false, [CallerMemberName] string propertyName = null) + { + if (compareRefOnly) + { + if (ReferenceEquals(field, value)) + return false; + } + else if (EqualityComparer.Default.Equals(field, value)) + { + return false; + } + + field = value; + OnPropertyChanged(propertyName); + return true; + } + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Diz.Core/model/Project.cs b/Diz.Core/model/Project.cs new file mode 100644 index 00000000..9b5a4d5c --- /dev/null +++ b/Diz.Core/model/Project.cs @@ -0,0 +1,148 @@ +using System; +using Diz.Core.export; +using Diz.Core.util; +using DiztinGUIsh; + +namespace Diz.Core.model +{ + public class Project : DizDataModel + { + // Any public properties will be automatically serialized to XML unless noted. + // They will require a get AND set. + // Order is important. + + // not saved in XML + public string ProjectFileName + { + get => projectFileName; + set => SetField(ref projectFileName, value); + } + + // not saved in XML + public string AttachedRomFilename + { + get => attachedRomFilename; + set => SetField(ref attachedRomFilename, value); + } + + // would be cool to make this more automatic. probably hook into SetField() + // for a lot of it. + public bool UnsavedChanges + { + get => unsavedChanges; + set => SetField(ref unsavedChanges, value); + } + + // safety checks: + // The rom "Game name" and "Checksum" are copies of certain bytes from the ROM which + // get stored with the project file. REMEMBER: We don't store the actual ROM bytes + // in the project file, so when we load a project, we must also open the same ROM and load its + // bytes in the project. + // + // Project = Metadata + // Rom = The real data + // + // If we load a ROM, and then its checksum and name don't match what we have stored, + // then we have an issue (i.e. not the same ROM, or it was modified, or missing, etc). + // The user must either provide the correct ROM, or abort loading the project. + public string InternalRomGameName + { + get => internalRomGameName; + set => SetField(ref internalRomGameName, value); + } + + public int InternalCheckSum + { + get => internalCheckSum; + set => SetField(ref internalCheckSum, value); + } + + public LogWriterSettings LogWriterSettings + { + get => logWriterSettings; + set => SetField(ref logWriterSettings, value); + } + + // purely visual. what offset is currently being looked at in the main grid. + // we store it here because we want to save it out with the project file + private int currentViewOffset; + public int CurrentViewOffset + { + get => currentViewOffset; + set => SetField(ref currentViewOffset, value); + } + + // needs to come last for serialization. this is the heart of the app, the actual + // data from the ROM and metadata we add/create. + public Data Data + { + get => data; + set => SetField(ref data, value); + } + + public Project() + { + LogWriterSettings.SetDefaults(); + } + + // don't access these backing fields directly, instead, always use the properties + private string projectFileName; + private string attachedRomFilename; + private bool unsavedChanges; + private string internalRomGameName; + private int internalCheckSum = -1; + private Data data; + private LogWriterSettings logWriterSettings; + + public string ReadRomIfMatchesProject(string filename, out byte[] romBytes) + { + string errorMsg = null; + + try { + romBytes = RomUtil.ReadAllRomBytesFromFile(filename); + if (romBytes != null) + { + errorMsg = IsThisRomIsIdenticalToUs(romBytes); + if (errorMsg == null) + return null; + } + } catch (Exception ex) { + errorMsg = ex.Message; + } + + romBytes = null; + return errorMsg; + } + + private string IsThisRomIsIdenticalToUs(byte[] romBytes) => + RomUtil.IsThisRomIsIdenticalToUs(romBytes, Data.RomMapMode, InternalRomGameName, InternalCheckSum); + + #region Equality + protected bool Equals(Project other) + { + return ProjectFileName == other.ProjectFileName && AttachedRomFilename == other.AttachedRomFilename && Equals(Data, other.Data) && InternalRomGameName == other.InternalRomGameName && InternalCheckSum == other.InternalCheckSum; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Project) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = (ProjectFileName != null ? ProjectFileName.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (AttachedRomFilename != null ? AttachedRomFilename.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Data != null ? Data.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (InternalRomGameName != null ? InternalRomGameName.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ InternalCheckSum; + return hashCode; + } + } + #endregion + } +} diff --git a/Diz.Core/model/ROMByte.cs b/Diz.Core/model/ROMByte.cs new file mode 100644 index 00000000..9bef9d9a --- /dev/null +++ b/Diz.Core/model/ROMByte.cs @@ -0,0 +1,113 @@ +using DiztinGUIsh; + +namespace Diz.Core.model +{ + public class RomByte : DizDataModel + { + // never modify directly. only go through the public fields + private byte rom; + private byte dataBank; + private int directPage; + private bool xFlag; + private bool mFlag; + private Data.FlagType typeFlag = Data.FlagType.Unreached; + private Data.Architecture arch = Data.Architecture.Cpu65C816; + private Data.InOutPoint point = 0; + + // holds the original byte from the source ROM + public byte Rom + { + get => rom; + set => SetField(ref rom, value); + } // never serialize this, read from ROM on load. (for copyright reasons) + + // everything else is metadata that describes the source byte above + public byte DataBank + { + get => dataBank; + set => SetField(ref dataBank, value); + } + + public int DirectPage + { + get => directPage; + set => SetField(ref directPage, value); + } + + public bool XFlag + { + get => xFlag; + set => SetField(ref xFlag, value); + } + + public bool MFlag + { + get => mFlag; + set => SetField(ref mFlag, value); + } + + public Data.FlagType TypeFlag + { + get => typeFlag; + set => SetField(ref typeFlag, value); + } + + public Data.Architecture Arch + { + get => arch; + set => SetField(ref arch, value); + } + + public Data.InOutPoint Point + { + get => point; + set => SetField(ref point, value); + } + + // don't serialize. cached copy of our offset in parent collection + public int Offset { get; private set; } = -1; + + public void SetCachedOffset(int offset) + { + // not in love with this or that we're caching it. would be cool if we didn't + // need Offset, or could just derive this (quickly) from the base list. + Offset = offset; + } + + + #region Equality + protected bool Equals(RomByte other) + { + return Rom == other.Rom && EqualsButNoRomByte(other); + } + + public bool EqualsButNoRomByte(RomByte other) + { + return DataBank == other.DataBank && DirectPage == other.DirectPage && XFlag == other.XFlag && MFlag == other.MFlag && TypeFlag == other.TypeFlag && Arch == other.Arch && Point == other.Point; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == this.GetType() && Equals((RomByte)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Rom.GetHashCode(); + hashCode = (hashCode * 397) ^ DataBank.GetHashCode(); + hashCode = (hashCode * 397) ^ DirectPage; + hashCode = (hashCode * 397) ^ XFlag.GetHashCode(); + hashCode = (hashCode * 397) ^ MFlag.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)TypeFlag; + hashCode = (hashCode * 397) ^ (int)Arch; + hashCode = (hashCode * 397) ^ (int)Point; + return hashCode; + } + } + #endregion + } +} diff --git a/Diz.Core/model/RomBytes.cs b/Diz.Core/model/RomBytes.cs new file mode 100644 index 00000000..368b0f2a --- /dev/null +++ b/Diz.Core/model/RomBytes.cs @@ -0,0 +1,128 @@ +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; + +namespace Diz.Core.model +{ + public class RomBytes : IEnumerable, INotifyCollectionChanged, INotifyPropertyChanged + { + private ObservableCollection bytes; + + // TODO: might be able to do something more generic now that other refactorings are completed. + // + // This class needs to do these things that are special: + // 1) Be handled specially by our custom XML serializer (compresses to save disk space) + // 2) Handle Equals() by comparing each element in the list (SequenceEqual) + private ObservableCollection Bytes + { + get => bytes; + set + { + bytes = value; + + bytes.CollectionChanged += Bytes_CollectionChanged; + foreach (var romByte in bytes) + { + romByte.PropertyChanged += RomByteObjectChanged; + } + } + } + + public RomByte this[int i] + { + get => Bytes[i]; + set => Bytes[i] = value; + } + + public RomBytes() + { + Bytes = new ObservableCollection(); + } + + public void SetFrom(RomByte[] romBytes) + { + Bytes = new ObservableCollection(romBytes); + for (var i = 0; i < romBytes.Length; ++i) + { + romBytes[i].SetCachedOffset(i); + } + } + + public int Count => Bytes.Count; + public bool SendNotificationChangedEvents { get; set; } = true; + + public void Add(RomByte romByte) + { + Bytes.Add(romByte); + romByte.SetCachedOffset(Bytes.Count - 1); // I don't love this.... + } + + public void Create(int size) + { + for (var i = 0; i < size; ++i) + Add(new RomByte()); + } + public void Clear() + { + Bytes.Clear(); + } + + #region Equality + protected bool Equals(RomBytes other) + { + return Bytes.SequenceEqual(other.Bytes); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((RomBytes)obj); + } + + public override int GetHashCode() + { + return (Bytes != null ? Bytes.GetHashCode() : 0); + } + #endregion + + #region Enumerator + public IEnumerator GetEnumerator() + { + return Bytes.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion + + public event NotifyCollectionChangedEventHandler CollectionChanged; + public event PropertyChangedEventHandler PropertyChanged; + + private void Bytes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.NewItems != null) + foreach (RomByte item in e.NewItems) + item.PropertyChanged += RomByteObjectChanged; + + if (e.OldItems != null) + foreach (RomByte item in e.OldItems) + item.PropertyChanged -= RomByteObjectChanged; + + if (SendNotificationChangedEvents) + CollectionChanged?.Invoke(sender, e); + } + + private void RomByteObjectChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (SendNotificationChangedEvents) + PropertyChanged?.Invoke(sender, e); + } + } +} diff --git a/Diz.Core/packages.config b/Diz.Core/packages.config new file mode 100644 index 00000000..0181b34a --- /dev/null +++ b/Diz.Core/packages.config @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Diz.Core/serialization/ImportSettings.cs b/Diz.Core/serialization/ImportSettings.cs new file mode 100644 index 00000000..9f22e6ad --- /dev/null +++ b/Diz.Core/serialization/ImportSettings.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Diz.Core.model; +using Diz.Core.util; +using DiztinGUIsh; + +namespace Diz.Core.serialization +{ + public class ImportRomSettings : PropertyNotifyChanged + { + private RomMapMode mode; + private RomSpeed romSpeed; + private byte[] romBytes; + private string romFilename; + + public RomMapMode RomMapMode + { + get => mode; + set => SetField(ref mode, value); + } + + public RomSpeed RomSpeed + { + get => romSpeed; + set => SetField(ref romSpeed, value); + } + + // todo: add INotify stuff if we care. probably dont need to. + public Dictionary InitialLabels { get; set; } + public Dictionary InitialHeaderFlags { get; set; } + + public byte[] RomBytes + { + get => romBytes; + set => SetField(ref romBytes, value); + } + + public string RomFilename + { + get => romFilename; + set => SetField(ref romFilename, value); + } + } +} \ No newline at end of file diff --git a/Diz.Core/serialization/ProjectFileManager.cs b/Diz.Core/serialization/ProjectFileManager.cs new file mode 100644 index 00000000..591b57e7 --- /dev/null +++ b/Diz.Core/serialization/ProjectFileManager.cs @@ -0,0 +1,160 @@ +using System; +using System.Diagnostics; +using System.IO; +using Diz.Core.model; +using Diz.Core.serialization.binary_serializer_old; +using Diz.Core.serialization.xml_serializer; +using Diz.Core.util; + +namespace Diz.Core.serialization +{ + public class ProjectFileManager : BaseProjectFileManager + { + public Func RomPromptFn { get; set; } + + // TODO: move romPromptFn to be a field instead of a param passed around. + protected override byte[] ReadFromOriginalRom(Project project) + { + string firstRomFileWeTried; + var nextFileToTry = firstRomFileWeTried = project.AttachedRomFilename; + byte[] rom; + + // try to open a ROM that matches us, if not, ask the user until they give up + do + { + var error = project.ReadRomIfMatchesProject(nextFileToTry, out rom); + if (error == null) + break; + + nextFileToTry = RomPromptFn(error); + if (nextFileToTry == null) + return null; + } while (true); + + project.AttachedRomFilename = nextFileToTry; + + if (project.AttachedRomFilename != firstRomFileWeTried) + project.UnsavedChanges = true; + + return rom; + } + } + + public abstract class BaseProjectFileManager + { + public (Project project, string warning) Open(string filename) + { + Trace.WriteLine("Opening Project START"); + + var data = File.ReadAllBytes(filename); + + if (!IsUncompressedProject(filename)) + data = Util.TryUnzip(data); + + var serializer = GetSerializerForFormat(data); + var result = serializer.Load(data); + + result.project.ProjectFileName = filename; + + PostSerialize(result.project); + + Trace.WriteLine("Opening Project END"); + return result; + } + + public bool PostSerialize(Project project) + { + // at this stage, 'Data' is populated with everything EXCEPT the actual ROM bytes. + // It would be easy to store the ROM bytes in the save file, but, for copyright reasons, + // we leave it out. + // + // So now, with all our metadata loaded successfully, we now open the .smc file on disk + // and marry the original rom's bytes with all of our metadata loaded from the project file. + + var data = project.Data; + + Debug.Assert(data.Labels != null && data.Comments != null); + Debug.Assert(data.RomBytes != null && data.RomBytes.Count > 0); + + var rom = ReadFromOriginalRom(project); + if (rom == null) + return false; + + data.CopyRomDataIn(rom); + return true; + } + + private static ProjectSerializer GetSerializerForFormat(byte[] data) + { + if (BinarySerializer.IsBinaryFileFormat(data)) + return new BinarySerializer(); + + return new ProjectXmlSerializer(); + } + + private static bool IsUncompressedProject(string filename) + { + return Path.GetExtension(filename).Equals(".dizraw", StringComparison.InvariantCultureIgnoreCase); + } + + public void Save(Project project, string filename) + { + // Everything saves in XML format from here on out. + // Binary format is deprecated. + ProjectSerializer defaultSerializer = new ProjectXmlSerializer(); + + Save(project, filename, defaultSerializer); + } + + private void Save(Project project, string filename, ProjectSerializer serializer) + { + var data = DoSave(project, filename, serializer); + + File.WriteAllBytes(filename, data); + project.UnsavedChanges = false; + project.ProjectFileName = filename; + } + + private byte[] DoSave(Project project, string filename, ProjectSerializer serializer) + { + var data = serializer.Save(project); + + if (!IsUncompressedProject(filename)) + data = Util.TryZip(data); + + return data; + } + + public static Project ImportRomAndCreateNewProject(ImportRomSettings importSettings) + { + var project = new Project + { + AttachedRomFilename = importSettings.RomFilename, + UnsavedChanges = false, + ProjectFileName = null, + Data = new Data() + }; + + project.Data.RomMapMode = importSettings.RomMapMode; + project.Data.RomSpeed = importSettings.RomSpeed; + project.Data.CreateRomBytesFromRom(importSettings.RomBytes); + + foreach (var pair in importSettings.InitialLabels) + project.Data.AddLabel(pair.Key, pair.Value, true); + + foreach (var pair in importSettings.InitialHeaderFlags) + project.Data.SetFlag(pair.Key, pair.Value); + + // Save a copy of these identifying ROM bytes with the project file itself. + // When we reload, we will make sure the linked ROM still matches them. + project.InternalCheckSum = project.Data.GetRomCheckSumsFromRomBytes(); + project.InternalRomGameName = project.Data.GetRomNameFromRomBytes(); + + project.UnsavedChanges = true; + + return project; + } + + protected abstract byte[] ReadFromOriginalRom(Project project); + } +} diff --git a/Diz.Core/serialization/ProjectSerializer.cs b/Diz.Core/serialization/ProjectSerializer.cs new file mode 100644 index 00000000..17772d57 --- /dev/null +++ b/Diz.Core/serialization/ProjectSerializer.cs @@ -0,0 +1,39 @@ +using System.Diagnostics; +using System.IO; +using Diz.Core.model; + +namespace Diz.Core.serialization +{ + public abstract class ProjectSerializer + { + public const string Watermark = "DiztinGUIsh"; + + public abstract byte[] Save(Project project); + public abstract (Project project, string warning) Load(byte[] data); + + public void SaveToFile(Project project, string filename) + { + File.WriteAllBytes(filename, Save(project)); + } + + public (Project project, string warning) LoadFromFile(string filename) + { + return Load(File.ReadAllBytes(filename)); + } + + protected static void DebugVerifyProjectEquality(Project project1, Project project2, bool deepCut = true) + { + if (deepCut) + { + for (var i = 0; i < project1.Data.RomBytes.Count; ++i) + { + Debug.Assert(project1.Data.RomBytes[i].EqualsButNoRomByte(project2.Data.RomBytes[i])); + } + + Debug.Assert(project1.Data.RomBytes.Equals(project2.Data.RomBytes)); + Debug.Assert(project1.Data.Equals(project2.Data)); + } + Debug.Assert(project1.Equals(project2)); + } + } +} diff --git a/Diz.Core/serialization/binary_serializer_old/BinarySerializer.cs b/Diz.Core/serialization/binary_serializer_old/BinarySerializer.cs new file mode 100644 index 00000000..d25c7392 --- /dev/null +++ b/Diz.Core/serialization/binary_serializer_old/BinarySerializer.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Diz.Core.model; +using Diz.Core.util; + +namespace Diz.Core.serialization.binary_serializer_old +{ + internal class BinarySerializer : ProjectSerializer + { + public const int HeaderSize = 0x100; + private const int LatestFileFormatVersion = 2; + + public static bool IsBinaryFileFormat(byte[] data) + { + for (var i = 0; i < Watermark.Length; i++) { + if (data[i + 1] != (byte) Watermark[i]) + return false; + } + return true; + } + + public override byte[] Save(Project project) + { + const int versionToSave = LatestFileFormatVersion; + var data = SaveVersion(project, versionToSave); + + var everything = new byte[HeaderSize + data.Length]; + everything[0] = versionToSave; + ByteUtil.StringToByteArray(Watermark).CopyTo(everything, 1); + data.CopyTo(everything, HeaderSize); + + return data; + } + + public override (Project project, string warning) Load(byte[] data) + { + if (!IsBinaryFileFormat(data)) + throw new InvalidDataException($"This is not a binary serialized project file!"); + + byte version = data[0]; + ValidateProjectFileVersion(version); + + var project = new Project + { + Data = new Data() + }; + + // version 0 needs to convert PC to SNES for some addresses + ByteUtil.AddressConverter converter = address => address; + if (version == 0) + converter = project.Data.ConvertPCtoSnes; + + // read mode, speed, size + project.Data.RomMapMode = (RomMapMode)data[HeaderSize]; + project.Data.RomSpeed = (RomSpeed)data[HeaderSize + 1]; + var size = ByteUtil.ByteArrayToInt32(data, HeaderSize + 2); + + // read internal title + var pointer = HeaderSize + 6; + project.InternalRomGameName = RomUtil.ReadStringFromByteArray(data, RomUtil.LengthOfTitleName, pointer); + pointer += RomUtil.LengthOfTitleName; + + // read checksums + project.InternalCheckSum = ByteUtil.ByteArrayToInt32(data, pointer); + pointer += 4; + + // read full filepath to the ROM .sfc file + while (data[pointer] != 0) + project.AttachedRomFilename += (char)data[pointer++]; + pointer++; + + project.Data.RomBytes.Create(size); + + for (int i = 0; i < size; i++) project.Data.SetDataBank(i, data[pointer + i]); + for (int i = 0; i < size; i++) project.Data.SetDirectPage(i, data[pointer + size + i] | (data[pointer + 2 * size + i] << 8)); + for (int i = 0; i < size; i++) project.Data.SetXFlag(i, data[pointer + 3 * size + i] != 0); + for (int i = 0; i < size; i++) project.Data.SetMFlag(i, data[pointer + 4 * size + i] != 0); + for (int i = 0; i < size; i++) project.Data.SetFlag(i, (Data.FlagType)data[pointer + 5 * size + i]); + for (int i = 0; i < size; i++) project.Data.SetArchitecture(i, (Data.Architecture)data[pointer + 6 * size + i]); + for (int i = 0; i < size; i++) project.Data.SetInOutPoint(i, (Data.InOutPoint)data[pointer + 7 * size + i]); + pointer += 8 * size; + + ReadLabels(project, data, ref pointer, converter, version >= 2); + ReadComments(project, data, ref pointer, converter); + + project.UnsavedChanges = false; + + var warning = ""; + if (version != LatestFileFormatVersion) + { + warning = "This project file is in an older format.\n" + + "You may want to back up your work or 'Save As' in case the conversion goes wrong.\n" + + "The project file will be untouched until it is saved again."; + } + + return (project, warning); + } + + private static void SaveStringToBytes(string str, ICollection bytes) + { + // TODO: combine with Util.StringToByteArray() probably. + if (str != null) { + foreach (var c in str) { + bytes.Add((byte)c); + } + } + bytes.Add(0); + } + + private byte[] SaveVersion(Project project, int version) + { + ValidateSaveVersion(version); + + int size = project.Data.GetRomSize(); + byte[] romSettings = new byte[31]; + + // save these two + romSettings[0] = (byte)project.Data.RomMapMode; + romSettings[1] = (byte)project.Data.RomSpeed; + + // save the size, 4 bytes + ByteUtil.IntegerIntoByteArray(size, romSettings, 2); + + var romName = project.Data.GetRomNameFromRomBytes(); + romName.ToCharArray().CopyTo(romSettings, 6); + + var romChecksum = project.Data.GetRomCheckSumsFromRomBytes(); + BitConverter.GetBytes(romChecksum).CopyTo(romSettings, 27); + + // TODO put selected offset in save file + + // save all labels ad comments + List label = new List(), comment = new List(); + var allLabels = project.Data.Labels; + var allComments = project.Data.Comments; + + ByteUtil.IntegerIntoByteList(allLabels.Count, label); + foreach (var pair in allLabels) + { + ByteUtil.IntegerIntoByteList(pair.Key, label); + + SaveStringToBytes(pair.Value.Name, label); + if (version >= 2) + { + SaveStringToBytes(pair.Value.Comment, label); + } + } + + ByteUtil.IntegerIntoByteList(allComments.Count, comment); + foreach (KeyValuePair pair in allComments) + { + ByteUtil.IntegerIntoByteList(pair.Key, comment); + SaveStringToBytes(pair.Value, comment); + } + + // save current Rom full path - "c:\whatever\someRom.sfc" + var romLocation = ByteUtil.StringToByteArray(project.AttachedRomFilename); + + var data = new byte[romSettings.Length + romLocation.Length + 8 * size + label.Count + comment.Count]; + romSettings.CopyTo(data, 0); + for (int i = 0; i < romLocation.Length; i++) data[romSettings.Length + i] = romLocation[i]; + + var readOps = new Func[] + { + i => (byte)project.Data.GetDataBank(i), + i => (byte)project.Data.GetDataBank(i), + i => (byte)project.Data.GetDirectPage(i), + i => (byte)(project.Data.GetDirectPage(i) >> 8), + i => (byte)(project.Data.GetXFlag(i) ? 1 : 0), + i => (byte)(project.Data.GetMFlag(i) ? 1 : 0), + i => (byte)project.Data.GetFlag(i), + i => (byte)project.Data.GetArchitecture(i), + i => (byte)project.Data.GetInOutPoint(i), + }; + + void ReadOperation(int startIdx, int whichOp) + { + if (whichOp <= 0 || whichOp > readOps.Length) + throw new ArgumentOutOfRangeException(nameof(whichOp)); + + var baseidx = startIdx + whichOp * size; + var op = readOps[whichOp]; + for (var i = 0; i < size; i++) + { + data[baseidx + i] = (byte)op(i); + } + } + + for (var i = 0; i < readOps.Length; ++i) + { + var start = romSettings.Length + romLocation.Length; + ReadOperation(start, i); + } + + // ??? + label.CopyTo(data, romSettings.Length + romLocation.Length + 8 * size); + comment.CopyTo(data, romSettings.Length + romLocation.Length + 8 * size + label.Count); + // ??? + + return data; + } + + private static void ValidateSaveVersion(int version) { + if (version < 1 || version > LatestFileFormatVersion) { + throw new ArgumentException($"Saving: Invalid save version requested for saving: {version}."); + } + } + + private static void ValidateProjectFileVersion(int version) + { + if (version > LatestFileFormatVersion) + { + throw new ArgumentException( + "This DiztinGUIsh file uses a newer file format! You'll need to download the newest version of DiztinGUIsh to open it."); + } + + if (version < 0) + { + throw new ArgumentException($"Invalid project file version detected: {version}."); + } + } + + private void ReadComments(Project project, byte[] bytes, ref int pointer, ByteUtil.AddressConverter converter) + { + const int stringsPerEntry = 1; + pointer += ByteUtil.ReadStringsTable(bytes, pointer, stringsPerEntry, converter, + (int offset, string[] strings) => + { + Debug.Assert(strings.Length == 1); + project.Data.AddComment(offset, strings[0], true); + }); + } + + private void ReadLabels(Project project, byte[] bytes, ref int pointer, ByteUtil.AddressConverter converter, bool readAliasComments) + { + var stringsPerEntry = readAliasComments ? 2 : 1; + pointer += ByteUtil.ReadStringsTable(bytes, pointer, stringsPerEntry, converter, + (int offset, string[] strings) => + { + Debug.Assert(strings.Length == stringsPerEntry); + var label = new Label + { + Name = strings[0], + Comment = strings.ElementAtOrDefault(1) + }; + label.CleanUp(); + project.Data.AddLabel(offset, label, true); + }); + } + } +} diff --git a/Diz.Core/serialization/xml_serializer/ProjectXMLSerializer.cs b/Diz.Core/serialization/xml_serializer/ProjectXMLSerializer.cs new file mode 100644 index 00000000..cb2aaaf2 --- /dev/null +++ b/Diz.Core/serialization/xml_serializer/ProjectXMLSerializer.cs @@ -0,0 +1,107 @@ +using System.IO; +using System.Text; +using System.Xml; +using Diz.Core.model; +using ExtendedXmlSerializer; + +namespace Diz.Core.serialization.xml_serializer +{ + public class ProjectXmlSerializer : ProjectSerializer + { + // NEVER CHANGE THIS ONE. + private const int FirstSaveFormatVersion = 100; + + // increment this if you change the XML file format + private const int CurrentSaveFormatVersion = FirstSaveFormatVersion; + + // update this if we are dropped support for really old save formats. + private const int EarliestSupportedSaveFormatVersion = FirstSaveFormatVersion; + + private class Root + { + // XML serializer specific metadata, top-level deserializer. + // This is unique to JUST the XML serializer, doesn't affect any other types of serializers. + // i.e. there is no global 'save format version' number, it's serializer-specific. + // + // NOTE: Please try and keep 'Root' unchanged and as generic as possible. It's way better + // to change 'Project' + public int SaveVersion { get; set; } = -1; + public string Watermark { get; set; } + public string Extra1 { get; set; } = ""; // reserved for future use + public string Extra2 { get; set; } = ""; // reserved for future use + + // The actual project itself. Almost any change you want to make should go in here. + public Project Project { get; set; } + }; + + public override byte[] Save(Project project) + { + // Wrap the project in a top-level root element with some info about the XML file + // format version. Note that each serializer has its own implementation of storing this metadata + var rootElement = new Root() + { + SaveVersion = CurrentSaveFormatVersion, + Watermark = ProjectSerializer.Watermark, + Project = project, + }; + + var xmlStr = XmlSerializerSupport.GetSerializer().Create().Serialize( + new XmlWriterSettings {Indent = true}, + rootElement); + + var finalBytes = Encoding.UTF8.GetBytes(xmlStr); + + // if you want some sanity checking, run this to verify everything saved correctly + // DebugVerifyProjectEquality(project, finalBytes); + // end debug + + return finalBytes; + } + + // just for debugging purposes, compare two projects together to make sure they serialize/deserialize + // correctly. + private void DebugVerifyProjectEquality(Project originalProjectWeJustSaved, byte[] projectBytesWeJustSerialized) + { + var result = Load(projectBytesWeJustSerialized); + var project2 = result.project; + + new ProjectFileManager().PostSerialize(project2); + DebugVerifyProjectEquality(originalProjectWeJustSaved, project2); + } + + public override (Project project, string warning) Load(byte[] data) + { + // TODO: it would be much more user-friendly/reliable if we could deserialize the + // Root element ALONE first, check for valid version/watermark, and only then try + // to deserialize the rest of the doc. + // + // Also, we can do data migrations based on versioning, and ExtendedXmlSerializer + + var text = Encoding.UTF8.GetString(data); + var root = XmlSerializerSupport.GetSerializer().Create().Deserialize(text); + + if (root.Watermark != Watermark) + throw new InvalidDataException( + "This file doesn't appear to be a valid DiztinGUIsh XML file (missing/invalid watermark element in XML)"); + + if (root.SaveVersion > CurrentSaveFormatVersion) + throw new InvalidDataException( + $"Save file version is newer than this version of DiztinGUIsh, likely can't be opened safely. This save file version = '{root.SaveVersion}', our highest supported version is {CurrentSaveFormatVersion}"); + + // Apply any migrations here for older save file formats. Right now, + // there aren't any because we're on the first revision. + // The XML serialization might be fairly forgiving of most kinds of changes, + // so you may not have to write migrations unless properties are renamed or deleted. + if (root.SaveVersion < CurrentSaveFormatVersion) + { + throw new InvalidDataException( + $"Save file version is newer than this version of DiztinGUIsh, likely can't be opened safely. This save file version = '{root.SaveVersion}', our highest supported version is {CurrentSaveFormatVersion}"); + } + + var project = root.Project; + var warning = ""; + + return (project, warning); + } + } +} \ No newline at end of file diff --git a/Diz.Core/serialization/xml_serializer/RepeaterCompression.cs b/Diz.Core/serialization/xml_serializer/RepeaterCompression.cs new file mode 100644 index 00000000..eee3b4ac --- /dev/null +++ b/Diz.Core/serialization/xml_serializer/RepeaterCompression.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace Diz.Core.serialization.xml_serializer +{ + public static class RepeaterCompression + { + public static void Decompress(ref List lines) + { + var output = new List(); + + foreach (var line in lines) + { + if (!line.StartsWith("r")) + { + output.Add(line); + continue; + } + + var split = line.Split(' '); + if (split.Length != 3) + throw new InvalidDataException("Invalid repeater command"); + + var count = int.Parse(split[1]); + for (var i = 0; i < count; ++i) + { + output.Add(split[2]); + } + } + + lines = output; + } + + public static void Compress(ref List lines) + { + if (lines.Count < 8) + return; // forget it, too small to care. + + var output = new List(); + + var lastLine = lines[0]; + var consecutive = 1; + + // adjustable, just pick something > 8 or it's not worth the optimization. + // we want to catch large consecutive blocks of data. + const int minNumberRepeatsBeforeWeBother = 8; + + var totalLinesDebug = 0; + + for (var i = 1; i < lines.Count; ++i) + { + var line = lines[i]; + Debug.Assert(!line.StartsWith("r")); + + var different = line != lastLine; + var finalLine = i == lines.Count - 1; + + if (!different) + { + consecutive++; + + if (!finalLine) + continue; + } + + if (consecutive >= minNumberRepeatsBeforeWeBother) + { + // replace multiple repeated lines with one new statement + output.Add($"r {consecutive.ToString()} {lastLine}"); + } + else + { + // output 1 or more copies of the last line + // this is also how we print single lines too + output.AddRange(Enumerable.Repeat(lastLine, consecutive).ToList()); + } + + if (finalLine && different) + { + output.Add(line); + totalLinesDebug++; + } + + totalLinesDebug += consecutive; + + lastLine = line; + consecutive = 1; + } + + Debug.Assert(totalLinesDebug == lines.Count); + + lines = output; + } + } +} diff --git a/Diz.Core/serialization/xml_serializer/RomByteEncoding.cs b/Diz.Core/serialization/xml_serializer/RomByteEncoding.cs new file mode 100644 index 00000000..41d3872a --- /dev/null +++ b/Diz.Core/serialization/xml_serializer/RomByteEncoding.cs @@ -0,0 +1,219 @@ +// #define EXTRA_DEBUG_CHECKS + +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Diz.Core.model; +using Diz.Core.util; + +namespace Diz.Core.serialization.xml_serializer +{ + public class RomByteEncoding + { + public class FlagEncodeEntry + { + public char C; + public Data.FlagType F; + }; + + // totally dumb but saves time vs slow Byte.Parse(x, isHex) + // idea credit: Daniel-Lemire + public static readonly int[] HexAsciiToDigit = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + public static byte ByteParseHex1(char hexChar) + { + var result = HexAsciiToDigit[hexChar]; + if (result == -1) + throw new InvalidDataException("Invalid hex digit"); + + return (byte)result; + } + + public static byte ByteParseHex2(char hexChar1, char hexChar2) + { + var result = ByteParseHex1(hexChar1) * 0x10 + ByteParseHex1(hexChar2); + return (byte)result; + } + + public static int ByteParseHex4(char hexChar1, char hexChar2, char hexChar3, char hexChar4) + { + return + ByteParseHex1(hexChar1) * 0x1000 + + ByteParseHex1(hexChar2) * 0x100 + + ByteParseHex1(hexChar3) * 0x10 + + ByteParseHex1(hexChar4); + } + + private static readonly List FlagEncodeTable = new List { + new FlagEncodeEntry() {F = Data.FlagType.Unreached, C = 'U'}, + + new FlagEncodeEntry() {F = Data.FlagType.Opcode, C = '+'}, + new FlagEncodeEntry() {F = Data.FlagType.Operand, C = '.'}, + + new FlagEncodeEntry() {F = Data.FlagType.Graphics, C = 'G'}, + new FlagEncodeEntry() {F = Data.FlagType.Music, C = 'M'}, + new FlagEncodeEntry() {F = Data.FlagType.Empty, C = 'X'}, + new FlagEncodeEntry() {F = Data.FlagType.Text, C = 'T'}, + + new FlagEncodeEntry() {F = Data.FlagType.Data8Bit, C = 'A'}, + new FlagEncodeEntry() {F = Data.FlagType.Data16Bit, C = 'B'}, + new FlagEncodeEntry() {F = Data.FlagType.Data24Bit, C = 'C'}, + new FlagEncodeEntry() {F = Data.FlagType.Data32Bit, C = 'D'}, + + new FlagEncodeEntry() {F = Data.FlagType.Pointer16Bit, C = 'E'}, + new FlagEncodeEntry() {F = Data.FlagType.Pointer24Bit, C = 'F'}, + new FlagEncodeEntry() {F = Data.FlagType.Pointer32Bit, C = 'G'}, + }; + + private readonly StringBuilder cachedPadSb = new StringBuilder(LineMaxLen); + + private const int LineMaxLen = 9; + + // note: performance-intensive function. be really careful when adding stuff here. + public RomByte DecodeRomByte(string line) + { + var input = PrepLine(line); + + var newByte = new RomByte(); + + var flagTxt = input[0]; + var otherFlags1 = Fake64Encoding.DecodeHackyBase64(input[1]); + newByte.DataBank = ByteParseHex2(input[2], input[3]); + newByte.DirectPage = ByteParseHex4(input[4], input[5], input[6], input[7]); + newByte.Arch = (Data.Architecture)(ByteParseHex1(input[8]) & 0x3); + + #if EXTRA_DEBUG_CHECKS + Debug.Assert(Fake64Encoding.EncodeHackyBase64(otherFlags1) == o1_str); + #endif + + newByte.XFlag = ((otherFlags1 >> 2) & 0x1) != 0; + newByte.MFlag = ((otherFlags1 >> 3) & 0x1) != 0; + newByte.Point = (Data.InOutPoint)((otherFlags1 >> 4) & 0xF); + + var found = false; + foreach (var e in FlagEncodeTable) + { + if (e.C != flagTxt) + continue; + + newByte.TypeFlag = e.F; + found = true; + break; + } + if (!found) + throw new InvalidDataException("Unknown FlagType"); + + return newByte; + } + + // perf note: re-uses a cached StringBuilder for subsequent runs + private StringBuilder PrepLine(string line) + { + if (cachedPadSb.Length == 0) + cachedPadSb.Append("000000000"); // any 9 chars + + // light decompression. ensure our line is always 9 chars long. + // if any characters are missing, pad them with zeroes + // + // perf: string.PadRight() is simpler but too slow, so do it by hand + var inSize = line.Length; + for (var i = 0; i < LineMaxLen; ++i) + { + cachedPadSb[i] = i < inSize ? line[i] : '0'; + } + + Debug.Assert(cachedPadSb.Length == LineMaxLen); + + return cachedPadSb; + } + + public string EncodeByte(RomByte instance) + { + // use a custom formatter here to save space. there are a LOT of ROMBytes. + // despite that we're still going for: + // 1) text only for slightly human readability + // 2) mergability in git/etc + // + // some of this can be unpacked further to increase readability without + // hurting the filesize too much. figure out what's useful. + // + // sorry, I know the encoding looks insane and weird and specific. this reduced my + // save file size from 42MB to less than 13MB + + // NOTE: must be uppercase letter or "=" or "-" + // if you add things here, make sure you understand the compression settings above. + var flagTxt = ' '; + foreach (var e in FlagEncodeTable) + { + if (e.F == instance.TypeFlag) + { + flagTxt = e.C; + break; + } + } + + if (flagTxt == ' ') + throw new InvalidDataException("Unknown FlagType"); + + // max 6 bits if we want to fit in 1 base64 ASCII digit + byte otherFlags1 = (byte)( + (instance.XFlag ? 1 : 0) << 2 | // 1 bit + (instance.MFlag ? 1 : 0) << 3 | // 1 bit + (byte)instance.Point << 4 // 4 bits + // LEAVE OFF THE LAST 2 BITS. it'll mess with the base64 below + ); + // reminder: when decoding, have to cut off all but the first 6 bits + var o1Str = Fake64Encoding.EncodeHackyBase64(otherFlags1); + Debug.Assert(Fake64Encoding.DecodeHackyBase64(o1Str) == otherFlags1); + + if (!instance.XFlag && !instance.MFlag && instance.Point == 0) + { + Debug.Assert(o1Str == '0'); // sanity + } + + // this is basically going to be "0" almost 100% of the time. + // we'll put it on the end of the string so it's most likely not output + byte otherFlags2 = (byte)( + (byte)instance.Arch << 0 // 2 bits + ); + var o2Str = otherFlags2.ToString("X1"); Debug.Assert(o2Str.Length == 1); + + // ordering: put DB and D on the end, they're likely to be zero and compressible + var sb = new StringBuilder(9); + sb.Append(flagTxt); + sb.Append(o1Str); + sb.Append(instance.DataBank.ToString("X2")); + sb.Append(instance.DirectPage.ToString("X4")); + sb.Append(o2Str); + + Debug.Assert(sb.Length == 9); + var data = sb.ToString(); + + // light compression: chop off any trailing zeroes. + // this alone saves a giant amount of space. + data = data.TrimEnd(new char[] { '0' }); + + // future compression but dilutes readability: + // if type is opcode or operand with same flags, combine those into 1 type. + // i.e. take an opcode with 3 operands, represent it using one digit in the file. + // instead of "=---", we swap with "+" or something. small optimization. + + return data; + } + } +} diff --git a/Diz.Core/serialization/xml_serializer/RomBytesXMLSerializer.cs b/Diz.Core/serialization/xml_serializer/RomBytesXMLSerializer.cs new file mode 100644 index 00000000..27a8806d --- /dev/null +++ b/Diz.Core/serialization/xml_serializer/RomBytesXMLSerializer.cs @@ -0,0 +1,212 @@ +// define to do some extra checking as we save the data out +// #define EXTRA_DEBUG_CHECKS + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Diz.Core.model; +using ExtendedXmlSerializer.ContentModel; +using ExtendedXmlSerializer.ContentModel.Format; + +// everything else in the savefiles is straight up normal XML, but, +// the amount of bytes for the ROM metadata can be huge. +// by using a custom serializer for this one section, we can cut a 4MB rom savefile down from ~45MB to ~1.5MB +// +// This uses some hacky compression methods designed to preserve: +// 1) newlines +// 2) slight human readability for merging, so projects can be collaborated on in source control +// 3) using pattern substitution tables that won't change from PC to PC. +// +// The easiest thing would be use zlib (and we can use it with the output of the entire XML if we want) +// but, for projects with multiple collaborators using diztinGUIsh, mergability in text/git/editors/etc +// is a must. We aim for a tradeoff between decent compression and some small semblance of human readability. +// +// It's not.. super-pretty code, but it compresses well. +namespace Diz.Core.serialization.xml_serializer +{ + sealed class RomBytesSerializer : ISerializer + { + // let the outer XML class do the heavy lifting on versioning. + // but, let's add one here just because this specific class is complex. + private const int CurrentDataFormatVersion = 200; + + public static RomBytesSerializer Default { get; } = new RomBytesSerializer(); + + private const bool CompressGroupBlock = true; + private const bool CompressUsingTable1 = true; + public int numTasksToUse = 5; // seems like the sweet spot + + public RomBytes Get(IFormatReader parameter) + { + var allLines = ReadMainDataRaw(parameter.Content()); + var romBytes = DecodeAllBytes(allLines); + return FinishRead(romBytes); + } + + private RomByte[] DecodeAllBytes(List allLines) + { + if (numTasksToUse == 1) + return DecodeRomBytes(allLines, 0, allLines.Count); + + var tasks = new List>(numTasksToUse); + + var nextIndex = 0; + var workListCount = allLines.Count / numTasksToUse; + + for (var t = 0; t < numTasksToUse; ++t) + { + if (t == numTasksToUse - 1) + workListCount = allLines.Count - nextIndex; + + tasks.Add(CreateDecodeRomBytesTask(allLines, nextIndex, workListCount)); + + nextIndex += workListCount; + } + + var continuation = Task.WhenAll(tasks); + continuation.Wait(); + return continuation.Result.SelectMany(i => i).ToArray(); + } + + private static Task CreateDecodeRomBytesTask(List allLines, int nextIndex, int workListCount) + { + // ReSharper disable once AccessToStaticMemberViaDerivedType + return Task.Run(() => DecodeRomBytes(allLines, nextIndex, workListCount)); + } + + private static RomByte[] DecodeRomBytes(IReadOnlyList allLines, int startIndex, int count) + { + // perf: allocate all at once, don't use List.Add() one at a time + var romBytes = new RomByte[count]; + var romByteEncoding = new RomByteEncoding(); + var i = 0; + + try + { + while (i < count) + { + var line = allLines[startIndex + i]; + romBytes[i] = romByteEncoding.DecodeRomByte(line); + ++i; + } + } + catch (Exception ex) + { + ex.Data.Add("ParseLineNum", "Near line# " + (startIndex + i)); + throw; + } + + return romBytes; + } + + private static RomBytes FinishRead(RomByte[] romBytes) + { + var romBytesOut = new RomBytes(); + romBytesOut.SetFrom(romBytes); + return romBytesOut; + } + + private static List ReadMainDataRaw(string allLines) + { + var (lines, options) = ReadHeader(allLines); + + CheckForCompatibleVersion(options); + + // always apply options in same order here and in saving function + if (options.Exists(s => s == "compress_table_1")) + SubstitutionCompression.DecodeCompression_Table1(ref lines); + + if (options.Exists(s => s == "compress_groupblocks")) + RepeaterCompression.Decompress(ref lines); + return lines; + } + + private static (List lines, List options) ReadHeader(string allLines) + { + var lines = allLines.Split(new char[] {'\n'}, 3).ToList(); + var options = lines[1].Split(new char[] {','}).ToList(); + lines = lines[2].Split(new char[] {'\n'}).ToList(); + if (lines[lines.Count - 1] == "") + lines.RemoveAt(lines.Count - 1); + return (lines, options); + } + + private static void CheckForCompatibleVersion(IEnumerable options) + { + try + { + var versionOption = options.SingleOrDefault(s => s.Contains("version:")); + + if (versionOption == null) + { + throw new InvalidDataException( + $"Exactly 1 'version' tag must be in options, unable to continue"); + } + + var split = versionOption.Split(':'); + Debug.Assert(split.Length == 2); + if (!int.TryParse(split[1], out var versionNum)) + throw new InvalidDataException( + $"Couldn't parse version # from version tag"); + + if (versionNum > CurrentDataFormatVersion) + throw new InvalidDataException( + $"Newer file format detected: {versionNum}. This version of distinguish only supports data table formats up to {CurrentDataFormatVersion}."); + + // In the future, we can add migrations here for older version. For now, just reject it. + if (versionNum < CurrentDataFormatVersion) + throw new InvalidDataException( + $"Newer file format detected: {versionNum}. This version of distinguish only supports data table formats up to {CurrentDataFormatVersion}."); + } + catch (Exception ex) + { + throw new InvalidDataException($"Data table loader: Version error: {ex.Message}"); + } + } + + public void Write(IFormatWriter writer, RomBytes instance) + { + var options = new List + { + $"version:{CurrentDataFormatVersion}", + }; + + var romByteEncoding = new RomByteEncoding(); + + var lines = new List(); + foreach (var rb in instance) + { + var encodedTxt = romByteEncoding.EncodeByte(rb); + lines.Add(encodedTxt); + + // debug check, optional: +#if EXTRA_DEBUG_CHECKS + var decoded = romByteEncoding.DecodeRomByte(encodedTxt); + Debug.Assert(decoded.EqualsButNoRomByte(rb)); +#endif + } + + if (CompressGroupBlock) + { + options.Add("compress_groupblocks"); + RepeaterCompression.Compress(ref lines); + } + + if (CompressUsingTable1) + { + options.Add("compress_table_1"); + SubstitutionCompression.EncodeCompression_Table1(ref lines); + } + + writer.Content($"\n{string.Join(",", options)}\n"); + + foreach (var line in lines) + { + writer.Content(line + "\n"); + } + } + } +} \ No newline at end of file diff --git a/Diz.Core/serialization/xml_serializer/SubstitutionCompression.cs b/Diz.Core/serialization/xml_serializer/SubstitutionCompression.cs new file mode 100644 index 00000000..50621f00 --- /dev/null +++ b/Diz.Core/serialization/xml_serializer/SubstitutionCompression.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Diz.Core.serialization.xml_serializer +{ + public static class SubstitutionCompression + { + private class CompressionEntry + { + public string LongTextPattern; + public string ShortTextToEncode; + } + + private static readonly List Table1 = new List + { + new CompressionEntry() {LongTextPattern = "0001E", ShortTextToEncode="ZQ"}, + new CompressionEntry() {LongTextPattern = "B0001", ShortTextToEncode="Zq"}, + new CompressionEntry() {LongTextPattern = "C0001", ShortTextToEncode="ZX"}, + new CompressionEntry() {LongTextPattern = "B7E", ShortTextToEncode="Zx"}, + new CompressionEntry() {LongTextPattern = "07F01", ShortTextToEncode="ZY"}, + new CompressionEntry() {LongTextPattern = "0001D", ShortTextToEncode="Zy"}, + new CompressionEntry() {LongTextPattern = "C7E", ShortTextToEncode="ZZ"}, + new CompressionEntry() {LongTextPattern = "07E", ShortTextToEncode="Zz"}, + new CompressionEntry() {LongTextPattern = "00001", ShortTextToEncode="ZS"}, + new CompressionEntry() {LongTextPattern = "0001", ShortTextToEncode="Zs"}, + }; + + public static void DecodeCompression_Table1(ref List lines) + { + for (int i = 0; i < lines.Count; ++i) + { + // shouldn't matter much but, apply in reverse to ensure it's done the same + // way as the encoding process + foreach (var e in Table1.Reverse()) + { + lines[i] = lines[i].Replace(e.ShortTextToEncode, e.LongTextPattern); + } + } + } + + public static void EncodeCompression_Table1(ref List lines) + { + // kind of a manually made / crappy huffman table encoding type thing. + // this is no great work of genius, more just some cheap hacks to reduce filesize. + // these are based on one half-assembled ROM i was using and probably don't + // universally work perfectly. I expect at some point we could collect a bunch of projects + // and make a Table2, add that here without having to bump the file version. + for (var i = 0; i < lines.Count; ++i) + { + foreach (var e in Table1) + { + Debug.Assert(!lines[i].Contains(e.ShortTextToEncode)); + lines[i] = lines[i].Replace(e.LongTextPattern, e.ShortTextToEncode); + } + } + } + } +} diff --git a/Diz.Core/serialization/xml_serializer/XMLSerializerSupport.cs b/Diz.Core/serialization/xml_serializer/XMLSerializerSupport.cs new file mode 100644 index 00000000..d7f091d2 --- /dev/null +++ b/Diz.Core/serialization/xml_serializer/XMLSerializerSupport.cs @@ -0,0 +1,33 @@ +using Diz.Core.model; +using ExtendedXmlSerializer; +using ExtendedXmlSerializer.Configuration; + +namespace Diz.Core.serialization.xml_serializer +{ + public static class XmlSerializerSupport + { + public static IConfigurationContainer GetSerializer() + { + // This configuration changes how parts of the data structures are serialized back/forth to XML. + // This is using the ExtendedXmlSerializer library, which has a zillion config options and is + // awesome. + // + // TODO: would be cool if these were stored as attributes on the classes themselves + return new ConfigurationContainer() + + .Type() + .Member(x => x.UnsavedChanges).Ignore() + .Member(x => x.ProjectFileName).Ignore() + + .Type() + .Register().Serializer().Using(RomBytesSerializer.Default) + + .Type() + .UseOptimizedNamespaces() + .UseAutoFormatting() + + .EnableImplicitTyping(typeof(Data)) + .EnableImplicitTyping(typeof(Label)); + } + } +} \ No newline at end of file diff --git a/Diz.Core/util/ByteUtil.cs b/Diz.Core/util/ByteUtil.cs new file mode 100644 index 00000000..3e2ef166 --- /dev/null +++ b/Diz.Core/util/ByteUtil.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Diz.Core.util +{ + public static class ByteUtil + { + // take input addresses that be in any formats that look like this, and convert to an int. + // This is useful if pasting addresses from other editors/tools/asm/etc trying to get a clean address. + // C0FFFF + // $C0FFFF + // C7/AAAA + // $C6/BBBB + public static bool StripFormattedAddress(ref string addressTxt, NumberStyles style, out int address) + { + address = -1; + + if (string.IsNullOrEmpty(addressTxt)) + return false; + + var inputText = new string(Array.FindAll(addressTxt.ToCharArray(), (c => + (char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)) + ))); + + if (!int.TryParse(inputText, style, null, out address)) + return false; + + addressTxt = inputText; + return true; + } + + public delegate int AddressConverter(int address); + + public static int ReadStringsTable(byte[] bytes, int startingIndex, int stringsPerEntry, AddressConverter converter, Action processTableEntry) + { + var strings = new List(); + + var pos = startingIndex; + var numTableEntries = ByteArrayToInt32(bytes, pos); + pos += 4; + + for (var entry = 0; entry < numTableEntries; ++entry) + { + var offset = converter(ByteArrayToInt32(bytes, pos)); + pos += 4; + + strings.Clear(); + for (var j = 0; j < stringsPerEntry; ++j) + { + pos += ReadNullTerminatedString(bytes, pos, out var str); + strings.Add(str); + } + processTableEntry(offset, strings.ToArray()); + } + + return pos - startingIndex; + } + + public static int ReadNullTerminatedString(byte[] bytes, int startingOffset, out string str) + { + str = ""; + var pos = startingOffset; + while (bytes[pos] != 0) + str += (char)bytes[pos++]; + pos++; + return pos - startingOffset; + } + + public static byte[] IntegerToByteArray(int a) + { + return new byte[] + { + (byte)a, + (byte)(a >> 8), + (byte)(a >> 16), + (byte)(a >> 24) + }; + } + + public static void IntegerIntoByteArray(int a, byte[] data, int offset) + { + byte[] arr = IntegerToByteArray(a); + for (int i = 0; i < arr.Length; i++) data[offset + i] = arr[i]; + } + + public static void IntegerIntoByteList(int a, List list) + { + byte[] arr = IntegerToByteArray(a); + foreach (var t in arr) + list.Add(t); + } + + public static int ByteArrayToInt32(byte[] data, int offset = 0) + { + return + data[offset] | + (data[offset + 1] << 8) | + (data[offset + 2] << 16) | + (data[offset + 3] << 24); + } + + public static int ByteArrayToInt24(byte[] data, int offset = 0) + { + return + data[offset] | + (data[offset + 1] << 8) | + (data[offset + 2] << 16); + } + + public static int ByteArrayToInt16(byte[] data, int offset = 0) + { + return + data[offset] | + (data[offset + 1] << 8); + } + + public static byte[] StringToByteArray(string s) + { + byte[] array = new byte[s.Length + 1]; + for (int i = 0; i < s.Length; i++) array[i] = (byte)s[i]; + array[s.Length] = 0; + return array; + } + } +} diff --git a/Diz.Core/util/Fake64Encoding.cs b/Diz.Core/util/Fake64Encoding.cs new file mode 100644 index 00000000..f1d71b1f --- /dev/null +++ b/Diz.Core/util/Fake64Encoding.cs @@ -0,0 +1,166 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Diz.Core.util +{ + // this is base64 EXCEPT: + // 1) char "A" and "0" swapped (my data format compresses things with "0" better) + // 2) only supports 6 bits of input data to encode on ONE BYTE. + // + // there's better ways to do this but I'm going for blinding speed for multi-threaded access. + // just, hardcode it. there are system Base64 functions we could use but, they're also way too slow. + // + // This is ... kind of a silly implementation... but, it's fast and works well with our compression so... + // if you don't like it that's just like... your opinion and stuff. man.♦ + // + // [don't judge me] -Dom + public static class Fake64Encoding + { + public static byte DecodeHackyBase64(char input) + { + return Fake64CharToByte[input]; + } + + public static char EncodeHackyBase64(byte input) + { + return Fake64ByteToChar[input]; + } + + // char to byte + private static readonly Dictionary Fake64CharToByte = new Dictionary() + { + {'A', 208}, + {'B', 4}, + {'C', 8}, + {'D', 12}, + {'E', 16}, + {'F', 20}, + {'G', 24}, + {'H', 28}, + {'I', 32}, + {'J', 36}, + {'K', 40}, + {'L', 44}, + {'M', 48}, + {'N', 52}, + {'O', 56}, + {'P', 60}, + {'Q', 64}, + {'R', 68}, + {'S', 72}, + {'T', 76}, + {'U', 80}, + {'V', 84}, + {'W', 88}, + {'X', 92}, + {'Y', 96}, + {'Z', 100}, + {'a', 104}, + {'b', 108}, + {'c', 112}, + {'d', 116}, + {'e', 120}, + {'f', 124}, + {'g', 128}, + {'h', 132}, + {'i', 136}, + {'j', 140}, + {'k', 144}, + {'l', 148}, + {'m', 152}, + {'n', 156}, + {'o', 160}, + {'p', 164}, + {'q', 168}, + {'r', 172}, + {'s', 176}, + {'t', 180}, + {'u', 184}, + {'v', 188}, + {'w', 192}, + {'x', 196}, + {'y', 200}, + {'z', 204}, + {'0', 0}, + {'1', 212}, + {'2', 216}, + {'3', 220}, + {'4', 224}, + {'5', 228}, + {'6', 232}, + {'7', 236}, + {'8', 240}, + {'9', 244}, + {'+', 248}, + {'/', 252}, + }; + + private static readonly Dictionary Fake64ByteToChar = new Dictionary() + { + {208, 'A'}, + {4, 'B'}, + {8, 'C'}, + {12, 'D'}, + {16, 'E'}, + {20, 'F'}, + {24, 'G'}, + {28, 'H'}, + {32, 'I'}, + {36, 'J'}, + {40, 'K'}, + {44, 'L'}, + {48, 'M'}, + {52, 'N'}, + {56, 'O'}, + {60, 'P'}, + {64, 'Q'}, + {68, 'R'}, + {72, 'S'}, + {76, 'T'}, + {80, 'U'}, + {84, 'V'}, + {88, 'W'}, + {92, 'X'}, + {96, 'Y'}, + {100, 'Z'}, + {104, 'a'}, + {108, 'b'}, + {112, 'c'}, + {116, 'd'}, + {120, 'e'}, + {124, 'f'}, + {128, 'g'}, + {132, 'h'}, + {136, 'i'}, + {140, 'j'}, + {144, 'k'}, + {148, 'l'}, + {152, 'm'}, + {156, 'n'}, + {160, 'o'}, + {164, 'p'}, + {168, 'q'}, + {172, 'r'}, + {176, 's'}, + {180, 't'}, + {184, 'u'}, + {188, 'v'}, + {192, 'w'}, + {196, 'x'}, + {200, 'y'}, + {204, 'z'}, + {0, '0'}, + {212, '1'}, + {216, '2'}, + {220, '3'}, + {224, '4'}, + {228, '5'}, + {232, '6'}, + {236, '7'}, + {240, '8'}, + {244, '9'}, + {248, '+'}, + {252, '/'}, + }; + } +} diff --git a/Diz.Core/util/RomUtil.cs b/Diz.Core/util/RomUtil.cs new file mode 100644 index 00000000..a721103a --- /dev/null +++ b/Diz.Core/util/RomUtil.cs @@ -0,0 +1,441 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using Diz.Core.export; +using Diz.Core.model; + +namespace Diz.Core.util +{ + public enum RomSpeed : byte + { + SlowRom, + FastRom, + Unknown + } + + public enum RomMapMode : byte + { + LoRom, + + HiRom, + + ExHiRom, + + [Description("SA - 1 ROM")] Sa1Rom, + + [Description("SA-1 ROM (FuSoYa's 8MB mapper)")] + ExSa1Rom, + + SuperFx, + + [Description("Super MMC")] SuperMmc, + + ExLoRom + } + + public static class RomUtil + { + public static int CalculateSnesOffsetWithWrap(int snesAddress, int offset) + { + return (GetBankFromSnesAddress(snesAddress) << 16) + ((snesAddress + offset) & 0xFFFF); + } + + private static int GetBankFromSnesAddress(int snesAddress) + { + return (snesAddress >> 16) & 0xFF; + } + + public static int GetBankSize(RomMapMode mode) + { + // todo + return mode == RomMapMode.LoRom ? 0x8000 : 0x10000; + } + + public static RomSpeed GetRomSpeed(int offset, IReadOnlyList romBytes) => + offset < romBytes.Count + ? (romBytes[offset] & 0x10) != 0 ? RomSpeed.FastRom : RomSpeed.SlowRom + : RomSpeed.Unknown; + + // verify the data in the provided ROM bytes matches the data we expect it to have. + // returns error message if it's not identical, or null if everything is OK. + public static string IsThisRomIsIdenticalToUs(byte[] rom, + RomMapMode mode, string requiredGameNameMatch, int requiredRomChecksumMatch) + { + var romSettingsOffset = GetRomSettingOffset(mode); + if (rom.Length <= romSettingsOffset + 10) + return "The linked ROM is too small. It can't be opened."; + + var internalGameNameToVerify = GetRomTitleName(rom, romSettingsOffset); + var checksumToVerify = ByteUtil.ByteArrayToInt32(rom, romSettingsOffset + 7); + + if (internalGameNameToVerify != requiredGameNameMatch) + return $"The linked ROM's internal name '{internalGameNameToVerify}' doesn't " + + $"match the project's internal name of '{requiredGameNameMatch}'."; + + if (checksumToVerify != requiredRomChecksumMatch) + return $"The linked ROM's checksums '{checksumToVerify:X8}' " + + $"don't match the project's checksums of '{requiredRomChecksumMatch:X8}'."; + + return null; + } + + public static string GetRomTitleName(byte[] rom, int romSettingOffset) + { + var offsetOfGameTitle = romSettingOffset - LengthOfTitleName; + var internalGameNameToVerify = ReadStringFromByteArray(rom, LengthOfTitleName, offsetOfGameTitle); + return internalGameNameToVerify; + } + + public static int ConvertSnesToPc(int address, RomMapMode mode, int size) + { + int UnmirroredOffset(int offset) => RomUtil.UnmirroredOffset(offset, size); + + // WRAM is N/A to PC addressing + if ((address & 0xFE0000) == 0x7E0000) return -1; + + // WRAM mirror & PPU regs are N/A to PC addressing + if (((address & 0x400000) == 0) && ((address & 0x8000) == 0)) return -1; + + switch (mode) + { + case RomMapMode.LoRom: + { + // SRAM is N/A to PC addressing + if (((address & 0x700000) == 0x700000) && ((address & 0x8000) == 0)) + return -1; + + return UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); + } + case RomMapMode.HiRom: + { + return UnmirroredOffset(address & 0x3FFFFF); + } + case RomMapMode.SuperMmc: + { + return UnmirroredOffset(address & 0x3FFFFF); // todo, treated as hirom atm + } + case RomMapMode.Sa1Rom: + case RomMapMode.ExSa1Rom: + { + // BW-RAM is N/A to PC addressing + if (address >= 0x400000 && address <= 0x7FFFFF) return -1; + + if (address >= 0xC00000) + return mode == RomMapMode.ExSa1Rom ? UnmirroredOffset(address & 0x7FFFFF) : UnmirroredOffset(address & 0x3FFFFF); + + if (address >= 0x800000) address -= 0x400000; + + // SRAM is N/A to PC addressing + if (((address & 0x8000) == 0)) return -1; + + return UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); + } + case RomMapMode.SuperFx: + { + // BW-RAM is N/A to PC addressing + if (address >= 0x600000 && address <= 0x7FFFFF) + return -1; + + if (address < 0x400000) + return UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); + + if (address < 0x600000) + return UnmirroredOffset(address & 0x3FFFFF); + + if (address < 0xC00000) + return 0x200000 + UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); + + return 0x400000 + UnmirroredOffset(address & 0x3FFFFF); + } + case RomMapMode.ExHiRom: + { + return UnmirroredOffset(((~address & 0x800000) >> 1) | (address & 0x3FFFFF)); + } + case RomMapMode.ExLoRom: + { + // SRAM is N/A to PC addressing + if (((address & 0x700000) == 0x700000) && ((address & 0x8000) == 0)) + return -1; + + return UnmirroredOffset((((address ^ 0x800000) & 0xFF0000) >> 1) | (address & 0x7FFF)); + } + default: + { + return -1; + } + } + } + + public static int ConvertPCtoSnes(int offset, RomMapMode romMapMode, RomSpeed romSpeed) + { + switch (romMapMode) + { + case RomMapMode.LoRom: + offset = ((offset & 0x3F8000) << 1) | 0x8000 | (offset & 0x7FFF); + if (romSpeed == RomSpeed.FastRom || offset >= 0x7E0000) offset |= 0x800000; + return offset; + case RomMapMode.HiRom: + offset |= 0x400000; + if (romSpeed == RomSpeed.FastRom || offset >= 0x7E0000) offset |= 0x800000; + return offset; + case RomMapMode.ExHiRom when offset < 0x40000: + offset |= 0xC00000; + return offset; + case RomMapMode.ExHiRom: + if (offset >= 0x7E0000) offset &= 0x3FFFFF; + return offset; + case RomMapMode.ExSa1Rom when offset >= 0x400000: + offset += 0x800000; + return offset; + } + + offset = ((offset & 0x3F8000) << 1) | 0x8000 | (offset & 0x7FFF); + if (offset >= 0x400000) offset += 0x400000; + + return offset; + } + + public static int UnmirroredOffset(int offset, int size) + { + // most of the time this is true; for efficiency + if (offset < size) + return offset; + + int repeatSize = 0x8000; + while (repeatSize < size) repeatSize <<= 1; + + int repeatedOffset = offset % repeatSize; + + // this will then be true for ROM sizes of powers of 2 + if (repeatedOffset < size) return repeatedOffset; + + // for ROM sizes not powers of 2, it's kinda ugly + int sizeOfSmallerSection = 0x8000; + while (size % (sizeOfSmallerSection << 1) == 0) sizeOfSmallerSection <<= 1; + + while (repeatedOffset >= size) repeatedOffset -= sizeOfSmallerSection; + return repeatedOffset; + } + + // TODO: these can be attributes on the enum itself. like [AsmLabel("UNREACH")] + public static string TypeToLabel(Data.FlagType flag) + { + return flag switch + { + Data.FlagType.Unreached => "UNREACH", + Data.FlagType.Opcode => "CODE", + Data.FlagType.Operand => "LOOSE_OP", + Data.FlagType.Data8Bit => "DATA8", + Data.FlagType.Graphics => "GFX", + Data.FlagType.Music => "MUSIC", + Data.FlagType.Empty => "EMPTY", + Data.FlagType.Data16Bit => "DATA16", + Data.FlagType.Pointer16Bit => "PTR16", + Data.FlagType.Data24Bit => "DATA24", + Data.FlagType.Pointer24Bit => "PTR24", + Data.FlagType.Data32Bit => "DATA32", + Data.FlagType.Pointer32Bit => "PTR32", + Data.FlagType.Text => "TEXT", + _ => "" + }; + } + + public static int GetByteLengthForFlag(Data.FlagType flag) + { + switch (flag) + { + case Data.FlagType.Unreached: + case Data.FlagType.Opcode: + case Data.FlagType.Operand: + case Data.FlagType.Data8Bit: + case Data.FlagType.Graphics: + case Data.FlagType.Music: + case Data.FlagType.Empty: + case Data.FlagType.Text: + return 1; + case Data.FlagType.Data16Bit: + case Data.FlagType.Pointer16Bit: + return 2; + case Data.FlagType.Data24Bit: + case Data.FlagType.Pointer24Bit: + return 3; + case Data.FlagType.Data32Bit: + case Data.FlagType.Pointer32Bit: + return 4; + } + return 0; + } + + public static RomMapMode DetectRomMapMode(IReadOnlyList romBytes, out bool detectedValidRomMapType) + { + detectedValidRomMapType = true; + + if ((romBytes[Data.LoromSettingOffset] & 0xEF) == 0x23) + return romBytes.Count > 0x400000 ? RomMapMode.ExSa1Rom : RomMapMode.Sa1Rom; + + if ((romBytes[Data.LoromSettingOffset] & 0xEC) == 0x20) + return (romBytes[Data.LoromSettingOffset + 1] & 0xF0) == 0x10 ? RomMapMode.SuperFx : RomMapMode.LoRom; + + if (romBytes.Count >= 0x10000 && (romBytes[Data.HiromSettingOffset] & 0xEF) == 0x21) + return RomMapMode.HiRom; + + if (romBytes.Count >= 0x10000 && (romBytes[Data.HiromSettingOffset] & 0xE7) == 0x22) + return RomMapMode.SuperMmc; + + if (romBytes.Count >= 0x410000 && (romBytes[Data.ExhiromSettingOffset] & 0xEF) == 0x25) + return RomMapMode.ExHiRom; + + // detection failed. take our best guess..... + detectedValidRomMapType = false; + return romBytes.Count > 0x40000 ? RomMapMode.ExLoRom : RomMapMode.LoRom; + } + + public static int GetRomSettingOffset(RomMapMode mode) + { + return mode switch + { + RomMapMode.LoRom => Data.LoromSettingOffset, + RomMapMode.HiRom => Data.HiromSettingOffset, + RomMapMode.ExHiRom => Data.ExhiromSettingOffset, + RomMapMode.ExLoRom => Data.ExloromSettingOffset, + _ => Data.LoromSettingOffset + }; + } + + public static string PointToString(Data.InOutPoint point) + { + string result; + + if ((point & Data.InOutPoint.EndPoint) == Data.InOutPoint.EndPoint) result = "X"; + else if ((point & Data.InOutPoint.OutPoint) == Data.InOutPoint.OutPoint) result = "<"; + else result = " "; + + result += ((point & Data.InOutPoint.ReadPoint) == Data.InOutPoint.ReadPoint) ? "*" : " "; + result += ((point & Data.InOutPoint.InPoint) == Data.InOutPoint.InPoint) ? ">" : " "; + + return result; + } + + public static string BoolToSize(bool b) + { + return b ? "8" : "16"; + } + + // read a fixed length string from an array of bytes. does not check for null termination + public static string ReadStringFromByteArray(byte[] bytes, int count, int offset) + { + var myName = ""; + for (var i = 0; i < count; i++) + myName += (char)bytes[offset + i]; + + return myName; + } + + public static byte[] ReadAllRomBytesFromFile(string filename) + { + var smc = File.ReadAllBytes(filename); + var rom = new byte[smc.Length & 0x7FFFFC00]; + + if ((smc.Length & 0x3FF) == 0x200) + // skip and dont include the SMC header + for (int i = 0; i < rom.Length; i++) + rom[i] = smc[i + 0x200]; + else if ((smc.Length & 0x3FF) != 0) + throw new InvalidDataException("This ROM has an unusual size. It can't be opened."); + else + rom = smc; + + if (rom.Length < 0x8000) + throw new InvalidDataException("This ROM is too small. It can't be opened."); + + return rom; + } + + public static void GenerateHeaderFlags(int romSettingsOffset, IDictionary flags, byte[] romBytes) + { + for (int i = 0; i < LengthOfTitleName; i++) + flags.Add(romSettingsOffset - LengthOfTitleName + i, Data.FlagType.Text); + + for (int i = 0; i < 7; i++) + flags.Add(romSettingsOffset + i, Data.FlagType.Data8Bit); + + for (int i = 0; i < 4; i++) + flags.Add(romSettingsOffset + 7 + i, Data.FlagType.Data16Bit); + + for (int i = 0; i < 0x20; i++) + flags.Add(romSettingsOffset + 11 + i, Data.FlagType.Pointer16Bit); + + if (romBytes[romSettingsOffset - 1] == 0) + { + flags.Remove(romSettingsOffset - 1); + flags.Add(romSettingsOffset - 1, Data.FlagType.Data8Bit); + for (int i = 0; i < 0x10; i++) + flags.Add(romSettingsOffset - 0x25 + i, Data.FlagType.Data8Bit); + } + else if (romBytes[romSettingsOffset + 5] == 0x33) + { + for (int i = 0; i < 6; i++) + flags.Add(romSettingsOffset - 0x25 + i, Data.FlagType.Text); + + for (int i = 0; i < 10; i++) + flags.Add(romSettingsOffset - 0x1F + i, Data.FlagType.Data8Bit); + } + } + + + public static Dictionary GenerateVectorLabels(Dictionary vectorNames, int romSettingsOffset, IReadOnlyList romBytes, RomMapMode mode) + { + // TODO: probably better to just use a data structure for this instead of generating the + // offsets with table/entry vars + + var labels = new Dictionary(); + + var baseOffset = romSettingsOffset + 15; + + var table = 0; const int tableCount = 2; + var entry = 0; const int entryCount = 6; + foreach (var vectorEntry in vectorNames) + { + Debug.Assert(table >= 0 && table < tableCount); + Debug.Assert(entry >= 0 && entry < entryCount); + // table = 0,1 // which table of Native vs Emulation + // entry = 0,1,2,3,4,5 // which offset + // + // 16*i = 16,32, + + var index = baseOffset + (16 * table) + (2 * entry); + var offset = romBytes[index] + (romBytes[index + 1] << 8); + var pc = ConvertSnesToPc(offset, mode, romBytes.Count); + if (pc >= 0 && pc < romBytes.Count && !labels.ContainsKey(offset)) + labels.Add(offset, new Label() { Name = vectorEntry.Key }); + + if (++entry < entryCount) + continue; + + entry = 0; + if (++table >= tableCount) + break; + } + + return labels; + } + + public const int LengthOfTitleName = 0x15; + + public static LogCreator.OutputResult GetSampleAssemblyOutput(LogWriterSettings sampleSettings) + { + var sampleRomData = SampleRomData.SampleData; + sampleSettings.Structure = LogCreator.FormatStructure.SingleFile; + sampleSettings.FileOrFolderOutPath = ""; + sampleSettings.OutputToString = true; + sampleSettings.RomSizeOverride = sampleRomData.OriginalRomSizeBeforePadding; + var lc = new LogCreator() + { + Settings = sampleSettings, + Data = sampleRomData, + }; + return lc.CreateLog(); + } + } +} diff --git a/Diz.Core/util/RomVisual.cs b/Diz.Core/util/RomVisual.cs new file mode 100644 index 00000000..bf253099 --- /dev/null +++ b/Diz.Core/util/RomVisual.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using Diz.Core.model; +using FastBitmapLib; + +namespace Diz.Core.util +{ + public class RomVisual + { + public event EventHandler ImageDataUpdated; + public event EventHandler MarkedDirty; + + public bool AutoRefresh { get; set; } = true; + + public Data Data => Project?.Data; + + public Project Project + { + get => project; + set + { + if (ReferenceEquals(project, value)) return; + SwitchProject(value); + } + } + + private void SwitchProject(Project value) + { + bitmap = null; + + project = value; + if (project?.Data == null) + return; + + project.Data.RomBytes.PropertyChanged += RomBytes_PropertyChanged; + project.Data.RomBytes.CollectionChanged += RomBytes_CollectionChanged; + } + + public bool IsDirty + { + get + { + lock (dirtyLock) + { + return AllDirty || + (AutoRefresh && dirtyRomBytes.Count > 0) || + bitmap == null; + } + } + } + + public Bitmap Bitmap + { + get + { + Refresh(); + return bitmap; + } + } + + public int RomStartingOffset + { + get => romStartingOffset; + set + { + if (value < 0 || value >= project.Data.RomBytes.Count) + throw new ArgumentOutOfRangeException(); + + romStartingOffset = value; + } + } + + public int LengthOverride + { + get => lengthOverride; + set + { + if (value != -1 && (value == 0 || RomStartingOffset + value > project.Data.RomBytes.Count)) + throw new ArgumentOutOfRangeException(); + + lengthOverride = value; + } + } + + public int PixelCount => lengthOverride != -1 ? lengthOverride : project.Data.RomBytes.Count; + + private int RomMaxOffsetAllowed => RomStartingOffset + PixelCount - 1; + + public int Width + { + get => width; + set + { + if (Width <= 0) + throw new ArgumentOutOfRangeException(); + + width = value; + } + } + + // this rounds up the height by one pixel if needed, meaning our last row can be incomplete if Width doesn't divide evenly into Length + public int Height => (int)Math.Ceiling(PixelCount / (double)Width); + + public bool AllDirty { get; set; } = true; + + private int romStartingOffset = 0; + private int lengthOverride = -1; + private int width = 1024; + private Bitmap bitmap; + private Project project; + + private readonly object dirtyLock = new object(); + private readonly Dictionary dirtyRomBytes = new Dictionary(); + + private void RomBytes_CollectionChanged(object sender, + System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + AllDirty = true; + } + + private bool OffsetInRange(int offset) + { + return (offset >= romStartingOffset && offset < romStartingOffset + lengthOverride); + } + + private void RomBytes_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (!(sender is RomByte romByte)) + return; + + if (e.PropertyName != "TypeFlag") + return; + + if (!OffsetInRange(romByte.Offset)) + return; + + MarkDirty(romByte); + } + + private void InvalidateImage() + { + bitmap = null; + AllDirty = true; + lock (dirtyLock) + { + dirtyRomBytes.Clear(); + } + + RegenerateImage(); + } + + public void Refresh() + { + if (IsDirty) + RegenerateImage(); + } + + private void RegenerateImage() + { + if (Data == null) + { + bitmap = null; + return; + } + + var h = Height; + var w = Width; + + var shouldRecreateBitmap = bitmap == null || bitmap.Width != w || bitmap.Height != h; + if (shouldRecreateBitmap) + bitmap = new Bitmap(w, h); + + var romBytes = ConsumeRomDirtyBytes(out var usedDirtyList); + var currentPixel = 0; + + var fastBitmap = new FastBitmap(bitmap); // needs compiler flag "/unsafe" enabled + using (fastBitmap.Lock()) + { + foreach (var romByte in romBytes) + { + SetPixel(romByte, fastBitmap); + ++currentPixel; + } + + if (!usedDirtyList) + { + // rom bytes may not fully fill up the last row. fill it in with + // blank pixels + while (currentPixel < w * h) + { + var (x, y) = ConvertPixelIndexToXy(currentPixel); + fastBitmap.SetPixel(x, y, Color.SlateGray); + ++currentPixel; + } + } + } + + AllDirty = false; + OnBitmapUpdated(); + } + + // returns the RomBytes we should use to update our image + // this can either be ALL RomBytes, or, a small set of dirty RomBytes that were changed + // since our last redraw. + private IEnumerable ConsumeRomDirtyBytes(out bool usedDirtyList) + { + usedDirtyList = false; + + if (AllDirty) + return Data.RomBytes + .Where(rb => + rb.Offset >= RomStartingOffset && rb.Offset <= RomMaxOffsetAllowed + ) + .ToList(); + + usedDirtyList = true; + IEnumerable romBytes; + lock (dirtyLock) + { + // make a copy so we can release the lock. + romBytes = new List(dirtyRomBytes.Values.Select(kvp => kvp)); + dirtyRomBytes.Clear(); + } + + return romBytes; + } + + private (int x, int y) ConvertPixelIndexToXy(int offset) + { + var y = offset / Width; + var x = offset - (y * Width); + return (x, y); + } + + private void SetPixel(RomByte romByte, FastBitmap fastBitmap) + { + var pixelIndex = ConvertRomOffsetToPixelIndex(romByte.Offset); + var (x, y) = ConvertPixelIndexToXy(pixelIndex); + var color = Util.GetColorFromFlag(romByte.TypeFlag); + fastBitmap.SetPixel(x, y, color); + } + + private int ConvertRomOffsetToPixelIndex(int romByteOffset) + { + return romByteOffset - RomStartingOffset; + } + + protected virtual void OnBitmapUpdated() + { + ImageDataUpdated?.Invoke(this, EventArgs.Empty); + } + + protected virtual void MarkDirty(RomByte romByte) + { + lock (dirtyLock) + { + if (!dirtyRomBytes.ContainsKey(romByte.Offset)) + dirtyRomBytes.Add(romByte.Offset, romByte); + else + dirtyRomBytes[romByte.Offset] = romByte; + } + + MarkedDirty?.Invoke(this, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/Diz.Core/util/Util.cs b/Diz.Core/util/Util.cs new file mode 100644 index 00000000..3292b746 --- /dev/null +++ b/Diz.Core/util/Util.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using Diz.Core.model; + +namespace Diz.Core.util +{ + public static class Util + { + public enum NumberBase + { + Decimal = 3, Hexadecimal = 2, Binary = 8 + } + + public static string NumberToBaseString(int v, NumberBase noBase, int d = -1, bool showPrefix = false) + { + var digits = d < 0 ? (int)noBase : d; + switch (noBase) + { + case NumberBase.Decimal: + return digits == 0 ? v.ToString("D") : v.ToString("D" + digits); + case NumberBase.Hexadecimal: + if (digits == 0) return v.ToString("X"); + return (showPrefix ? "$" : "") + v.ToString("X" + digits); + case NumberBase.Binary: + var b = ""; + var i = 0; + while (digits == 0 ? v > 0 : i < digits) + { + b += (v & 1); + v >>= 1; + i++; + } + return (showPrefix ? "%" : "") + b; + } + return ""; + } + + public static IEnumerable ReadLines(string path) + { + using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan); + using var sr = new StreamReader(fs, Encoding.UTF8); + + string line; + while ((line = sr.ReadLine()) != null) + { + yield return line; + } + } + + public static long GetFileSizeInBytes(string filename) + { + var fi = new FileInfo(filename); + if (!fi.Exists) + return -1; + + return fi.Length; + } + + // https://stackoverflow.com/questions/33119119/unzip-byte-array-in-c-sharp + public static byte[] TryUnzip(byte[] data) + { + try + { + using var comp = new MemoryStream(data); + using var gzip = new GZipStream(comp, CompressionMode.Decompress); + using var res = new MemoryStream(); + gzip.CopyTo(res); + return res.ToArray(); + } + catch (Exception) + { + return null; + } + } + + public static byte[] TryZip(byte[] data) + { + try + { + using var comp = new MemoryStream(); + using var gzip = new GZipStream(comp, CompressionMode.Compress); + gzip.Write(data, 0, data.Length); + gzip.Close(); + return comp.ToArray(); + } + catch (Exception) + { + return null; + } + } + + public static byte[] ReadNext(Stream stream, int count, out int bytesRead) + { + var buffer = new byte[count]; + bytesRead = 0; + var offset = 0; + + while (count > 0 && (bytesRead = stream.Read(buffer, offset, count)) > 0) + { + count -= bytesRead; + offset += bytesRead; + } + + if (count > 0) + throw new EndOfStreamException(); + + return buffer; + } + + public static string GetEnumDescription(Enum value) + { + // example: + // type = Data.ROMMapMode (the entire enum) + // value = ExSA1ROM (one particular entry from the enum) + // description = "SA-1 ROM (FuSoYa's 8MB mapper)", (contents of [Description] attribute) + + return GetEnumAttribute( + value, + (DescriptionAttribute d) => d?.Description + ) ?? value.ToString(); + } + + public static TResult GetEnumAttribute(Enum value, Func getValueFn) where TAttribute : Attribute + { + var type = value.GetType(); + var memberInfo = type.GetField(value.ToString()); + var descAttr = ((TAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(TAttribute))); + return getValueFn(descAttr); + } + + // take a enum type that has [Description] attributes, + // return a List with with kvp pairs of enum vs description + public static List> GetEnumDescriptions() where TEnum : Enum + { + return GetEnumInfo((value) => GetEnumDescription(value)); + } + + // perf: might be a little slow, caution when in tight loops + public static List> GetEnumColorDescriptions() where TEnum : Enum + { + return GetEnumInfo((value) => GetEnumColor(value)); + } + + private static KnownColor GetEnumColor(Enum value) + { + return GetEnumAttribute( + value, + (Data.ColorDescriptionAttribute d) => d?.Color + ) ?? KnownColor.Black; + } + + public static List> GetEnumInfo(Func getValue) where TEnum : Enum + { + var type = typeof(TEnum); + return Enum.GetValues(type) + .Cast() + .Select(value => new + KeyValuePair(key: value, value: getValue(value)) + ) + .OrderBy(item => item.Key) + .ToList(); + } + + // sadly, this entire conversion is a bit slow so, cache it as we look it up + private static readonly Dictionary CachedRomFlagColors = + new Dictionary(); + + public static Color GetColorFromFlag(Data.FlagType romFlag) + { + if (CachedRomFlagColors.TryGetValue(romFlag, out var color)) + return color; + + color = Color.FromKnownColor(GetEnumColor(romFlag)); // slow (comparatively) + CachedRomFlagColors[romFlag] = color; + + return color; + } + } +} diff --git a/Diz.Core/util/WorkerTaskManager.cs b/Diz.Core/util/WorkerTaskManager.cs new file mode 100644 index 00000000..d3f81059 --- /dev/null +++ b/Diz.Core/util/WorkerTaskManager.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Diz.Core.util +{ + public class WorkerTaskManager + { + private readonly List tasks = new List(); + private readonly object taskLock = new object(); + + private readonly ManualResetEvent notFinishing = new ManualResetEvent(false); + private volatile bool finished = false; + private Timer timer; + + public WorkerTaskManager() + { + var oneSecond = TimeSpan.FromSeconds(1); + timer = new Timer(state => Update(), null, oneSecond, oneSecond); + } + + public void StartFinishing() + { + notFinishing.Set(); + } + + public void Update() + { + if (finished) + { + timer.Change(-1, -1); + timer.Dispose(); + timer = null; + } + + lock (tasks) + tasks.RemoveAll(IsCompleted); + } + + public Task Run(Action action) + { + var task = new Task(action); + + lock (taskLock) + tasks.Add(task); + + task.Start(); + + return task; + } + + private static bool IsCompleted(Task task) => task.IsCompleted; + + // task-oriented version of below + /*public Task RunTask_WaitForAllTasksToComplete() + { + return Task.Run(WaitForAllTasksToComplete); + }*/ + + // blocking method + public void WaitForAllTasksToComplete() + { + // wait until we've been told we should be finishing + notFinishing.WaitOne(); + + while (true) + { + List tasksCopy; + lock (taskLock) + { + if (tasks.Count == 0) + break; + + tasksCopy = new List(tasks); + } + + // anything in our original list, let it run to completion + Task.WaitAll(tasksCopy.ToArray()); + } + + finished = true; + } + } +} \ No newline at end of file diff --git a/Diz.Test/CompressionTest.cs b/Diz.Test/CompressionTest.cs new file mode 100644 index 00000000..1dce203e --- /dev/null +++ b/Diz.Test/CompressionTest.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.Linq; +using Diz.Core.serialization.xml_serializer; +using Xunit; + +namespace Diz.Test +{ + public static class CompressionTest + { + private static IEnumerable Repeat(TOut toRepeat, int times) { + for (var i = 0; i < times; ++i) { + yield return toRepeat; + } + } + + private static (IEnumerable, IEnumerable) GenerateRepeat(string toRepeat, int times) + { + var repeated = Repeat($"{toRepeat}", times); + return (repeated.ToList(), + times >= 8 + ? new List(new[] {$"r {times} {toRepeat}"}) + : repeated.ToList() + ); + } + + public static TheoryData, IEnumerable> ValidCompressionData + { + get + { + // TODO: probably a way simpler way to do this. works ok. + var p = ValidDataReal(); + var xx = new TheoryData, IEnumerable>(); + p.ForEach(i => xx.Add(i.Item1, i.Item2)); + return xx; + } + } + + private static List<(IEnumerable, IEnumerable)> ValidDataReal() => + new List<(IEnumerable, IEnumerable)> + { + GenerateRepeat("TestItem", 20), + GenerateRepeat("TestItem", 30), + ( + Repeat("YO", 20).Concat(new[] { + "different @ end" + }), + new List(new[] { + "r 20 YO", + "different @ end" + }) + ), + ( + new List() + { + "start", + "start2", + } + .Concat( + Repeat("YO1", 22).Concat( + Repeat("YO2", 20).Concat( + new[] { + "different @ end" + }))), + new List(new[] { + "start", + "start2", + "r 22 YO1", + "r 20 YO2", + "different @ end" + }) + ) + }; + + [Theory] + [MemberData(nameof(ValidCompressionData))] + public static void TestCompressionsValid(IEnumerable input, IEnumerable expected) + { + var inputListCopy = new List(input); + RepeaterCompression.Compress(ref inputListCopy); + Assert.Equal(expected, inputListCopy); + + RepeaterCompression.Decompress(ref inputListCopy); + Assert.Equal(inputListCopy, input); + } + } +} \ No newline at end of file diff --git a/Diz.Test/Diz.Test.csproj b/Diz.Test/Diz.Test.csproj new file mode 100644 index 00000000..f5d12f0d --- /dev/null +++ b/Diz.Test/Diz.Test.csproj @@ -0,0 +1,175 @@ + + + + + + + + Debug + AnyCPU + {594DE30A-343F-4C09-828D-65A67626A491} + Library + Properties + Diz.Test + Diz.Test + v4.8 + 512 + true + 8.0 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + + + ..\packages\ExtendedXmlSerializer.3.4.1\lib\net452\ExtendedXmlSerializer.dll + + + ..\packages\IX.Abstractions.0.5.3\lib\net472\IX.Abstractions.dll + + + ..\packages\IX.Abstractions.Collections.0.5.3\lib\net472\IX.Abstractions.Collections.dll + + + ..\packages\IX.Abstractions.Threading.0.5.3\lib\net472\IX.Abstractions.Threading.dll + + + ..\packages\IX.Observable.0.5.3\lib\net472\IX.Observable.dll + + + ..\packages\IX.StandardExtensions.0.5.3\lib\net472\IX.StandardExtensions.dll + + + ..\packages\IX.StandardExtensions.ComponentModel.0.5.3\lib\net472\IX.StandardExtensions.ComponentModel.dll + + + ..\packages\IX.StandardExtensions.Threading.0.5.3\lib\net472\IX.StandardExtensions.Threading.dll + + + ..\packages\IX.Undoable.0.5.3\lib\net472\IX.Undoable.dll + + + ..\packages\JetBrains.Annotations.2019.1.3\lib\net20\JetBrains.Annotations.dll + + + ..\packages\LightInject.6.3.5\lib\net46\LightInject.dll + + + ..\packages\Moq.4.14.6\lib\net45\Moq.dll + + + ..\packages\NReco.LambdaParser.1.0.12\lib\net45\NReco.LambdaParser.dll + + + ..\packages\Sprache.2.3.1\lib\net45\Sprache.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll + + + + + + ..\packages\System.Interactive.4.1.1\lib\net45\System.Interactive.dll + + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + + ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + ..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll + + + + + + + + + + + + + + + + + + {50646b00-03d8-4cec-9ed1-3ef4cb199e8b} + Diz.Core + + + {2a2dd3c1-9e64-4cd7-98a5-310d9fed2ca3} + DiztinGUIsh + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/Diz.Test/LoadSaveTest.cs b/Diz.Test/LoadSaveTest.cs new file mode 100644 index 00000000..31e7b32c --- /dev/null +++ b/Diz.Test/LoadSaveTest.cs @@ -0,0 +1,89 @@ +using System.Diagnostics; +using Diz.Core; +using Diz.Core.model; +using Diz.Core.serialization; +using Diz.Core.serialization.xml_serializer; +using Xunit; +using Xunit.Abstractions; + +namespace Diz.Test +{ + public class LoadSaveTest + { + [Fact] + private void FullSerializeAndDeserialize() + { + // use the sample data to fake a project + var sampleProject = new Project {Data = SampleRomData.SampleData}; + + // extract the bytes that would normally be in the SMC file (they only exist in code for this sample data) + var romFileBytes = sampleProject.Data.GetFileBytes(); + + // save it to create an output byte stream, we'd normally write this to the disk + var serializer = new ProjectXmlSerializer(); + var outputBytes = serializer.Save(sampleProject); + + // now do the reverse and load our output back as the input + var (deserializedProject, warning) = serializer.Load(outputBytes); + + // final step, the loading process doesn't save the actual SMC file bytes, so we do it ourselves here + deserializedProject.Data.CopyRomDataIn(romFileBytes); + + // now we can do a full compare between the original project, and the project which has been cycled through + // serialization and deserialization + Assert.True(warning == ""); + Assert.True(sampleProject.Equals(deserializedProject)); + } + + private readonly ITestOutputHelper output; + + public LoadSaveTest(ITestOutputHelper output) + { + this.output = output; + } + + private static Project OpenProject(string openFile) + { + var projectFileManager = new ProjectFileManager(); + var (project, warning) = projectFileManager.Open(openFile); + + Assert.Equal("", warning); + Assert.True(project.Data.RomBytes.Count >= 0x1000 * 64); + + return project; + } + + [Fact(Skip = "Performance Test")] + private void OpenFilePerformanceTest() + { + var s = Stopwatch.StartNew(); + s.Start(); + + var openFile = "D:\\projects\\cthack\\src\\rom\\Chrono Trigger - CTHACKv3 - master copy.xml.dizraw"; + var project = OpenProject(openFile); + + s.Stop(); + + output.WriteLine($"runtime: {s.ElapsedMilliseconds:N0}, #bytes={project.Data.RomBytes.Count}"); + } + + + [Fact(Skip = "Performance Test")] + private void SaveFilePerformanceTest() + { + var openFile = "INSERT YOUR FILE HERE BEFORE RUNNING THIS TEST"; + var project = OpenProject(openFile); + + var s = Stopwatch.StartNew(); + s.Start(); + + var data = new ProjectXmlSerializer().Save(project); + + s.Stop(); + + Assert.True(data.Length != 0); + + output.WriteLine($"runtime: {s.ElapsedMilliseconds:N0}"); + } + } +} \ No newline at end of file diff --git a/Diz.Test/LogCreator.cs b/Diz.Test/LogCreator.cs new file mode 100644 index 00000000..8b2eb145 --- /dev/null +++ b/Diz.Test/LogCreator.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Diz.Core.export; +using Diz.Core.model; +using Diz.Core.util; +using IX.Observable; +using Xunit; + +namespace Diz.Test +{ + public sealed class LogCreatorTests + { + public class ParsedOutput + { + protected bool Equals(ParsedOutput other) + { + return Label == other.Label && Instr == other.Instr && Pc == other.Pc && Rawbytes == other.Rawbytes && Ia == other.Ia && Realcomment == other.Realcomment; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ParsedOutput) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Label.GetHashCode(); + hashCode = (hashCode * 397) ^ Instr.GetHashCode(); + hashCode = (hashCode * 397) ^ Pc.GetHashCode(); + hashCode = (hashCode * 397) ^ Rawbytes.GetHashCode(); + hashCode = (hashCode * 397) ^ Ia.GetHashCode(); + hashCode = (hashCode * 397) ^ Realcomment.GetHashCode(); + return hashCode; + } + } + + public string Label; + public string Instr; + + public string Pc; + public string Rawbytes; + public string Ia; + + public string Realcomment; + } + + public static ParsedOutput ParseLine(string line) + { + if (line == "") + return null; + + var output = new ParsedOutput(); + + var split = line.Split(new[] {';'}, 2,options:StringSplitOptions.None); + var main = split[0].Trim(); + var comment = split[1].Trim(); + + var csplit = comment.Split(new[] { '|' }, 3, options: StringSplitOptions.None); + output.Pc = csplit[0].Trim(); + output.Rawbytes = csplit[1].Trim(); + + var iasplit = csplit[2].Split(new[] { ';' }, 2, options: StringSplitOptions.None); + output.Ia = iasplit[0].Trim(); + output.Realcomment = iasplit[1].Trim(); + + var msplit = main.Split(new[] { ':' }, 2, options: StringSplitOptions.None); + var m1 = msplit[0].Trim(); + var m2 = msplit.Length > 1 ? msplit[1].Trim() : ""; + + if (m2 != "") + { + output.Label = m1; + output.Instr = m2; + } + else + { + output.Label = ""; + output.Instr = m1; + } + + return output; + } + + public List ParseAll(string lines) => + lines.Split(new[] {'\n'}) + .Select(line => ParseLine(line.Trim())) + .ToList(); + + [Fact] + public void TestAFewLines() + { + var expectedRaw = + // label: instructions ;PC |rawbytes|ia + " lorom ; | | ; \r\n" + + " ; | | ; \r\n" + + " ; | | ; \r\n" + + " ORG $808000 ; | | ; \r\n" + + " ; | | ; \r\n" + + " CODE_808000: LDA.W Test_Data,X ;808000|BD5B80 |80805B; \r\n" + + " STA.W $0100,X ;808003|9D0001 |800100; \r\n" + + " Test22: DEX ;808006|CA | ; \r\n" + + " BPL CODE_808000 ;808007|10F7 |808000; \r\n" + + " ; | | ; \r\n" + + " Test_Data = $80805B ; | | ; \r\n"; + + var expectedOut = ParseAll(expectedRaw); + + var inputData = new Data + { + Labels = new ObservableDictionary + { + {0x808000 + 0x06, new Label {Name = "Test22"}}, + {0x808000 + 0x5B, new Label {Name = "Test_Data", Comment = "Pretty cool huh?"}}, + // the CODE_XXXXXX labels are autogenerated + }, + RomMapMode = RomMapMode.LoRom, + RomSpeed = RomSpeed.FastRom, + RomBytes = + { + // -------------------------- + // highlighting a particular section here + // we will use this for unit tests as well. + + // CODE_808000: LDA.W Test_Data,X + new RomByte {Rom = 0xBD, TypeFlag = Data.FlagType.Opcode, MFlag = true, Point = Data.InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x5B, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, // Test_Data + new RomByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, // Test_Data + + // STA.W $0100,X + new RomByte {Rom = 0x9D, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0x01, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + + // DEX + new RomByte {Rom = 0xCA, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, + + // BPL CODE_808000 + new RomByte {Rom = 0x10, TypeFlag = Data.FlagType.Opcode, MFlag = true, Point = Data.InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, + new RomByte {Rom = 0xF7, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, + + // ------------------------------------ + } + }; + + var settings = new LogWriterSettings(); + settings.SetDefaults(); + settings.OutputToString = true; + settings.Structure = LogCreator.FormatStructure.SingleFile; + + var lc = new LogCreator() + { + Data=inputData, + Settings = settings, + }; + + var result = lc.CreateLog(); + + Assert.True(result.LogCreator != null); + Assert.True(result.OutputStr != null); + Assert.True(result.ErrorCount == 0); + + var actualOut = ParseAll(result.OutputStr); + + Assert.Equal(expectedOut.Count, actualOut.Count); + + for (var i = 0; i < expectedOut.Count; ++i) + { + Assert.Equal(expectedOut[i], actualOut[i]); + } + + Assert.True(expectedOut.SequenceEqual(actualOut)); + } + } +} \ No newline at end of file diff --git a/Diz.Test/MiscTests.cs b/Diz.Test/MiscTests.cs new file mode 100644 index 00000000..925b937a --- /dev/null +++ b/Diz.Test/MiscTests.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; +using Diz.Core.serialization.xml_serializer; +using Diz.Core.util; +using Xunit; + +namespace Diz.Test +{ + public static class MiscTests + { + [Fact] + public static void TestHex() + { + Assert.Equal(0xF0, RomByteEncoding.ByteParseHex2('F', '0')); + } + + [Fact] + public static void TestHex4() + { + Assert.Equal(0xF029, RomByteEncoding.ByteParseHex4('F', '0', '2', '9')); + } + } +} \ No newline at end of file diff --git a/Diz.Test/Properties/AssemblyInfo.cs b/Diz.Test/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..84fa07a0 --- /dev/null +++ b/Diz.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Diz.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Diz.Test")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("594de30a-343f-4c09-828d-65a67626a491")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Diz.Test/RomByteTests.cs b/Diz.Test/RomByteTests.cs new file mode 100644 index 00000000..65ab97c3 --- /dev/null +++ b/Diz.Test/RomByteTests.cs @@ -0,0 +1,54 @@ +using Diz.Core.model; +using Xunit; + +namespace Diz.Test +{ + public sealed class RomByteTests + { + private static RomByte SampleRomByte1() + { + return new RomByte() { + Arch = Data.Architecture.Apuspc700, + DataBank = 90, + DirectPage = 3, + MFlag = true, + XFlag = false, + TypeFlag = Data.FlagType.Graphics, + Point = Data.InOutPoint.InPoint | Data.InOutPoint.ReadPoint, + Rom = 0x78, + }; + } + + private static RomByte SampleRomByte2() + { + // same as above, but just change .Rom + var rb = SampleRomByte1(); + rb.Rom = 0x99; + return rb; + } + + [Fact] + public void TestEqualsButNotCompareByte() + { + var rb1 = SampleRomByte1(); + var rb2 = SampleRomByte2(); + + Assert.True(rb1.EqualsButNoRomByte(rb2)); + Assert.False(rb1.Equals(rb2)); + + rb1.Point = Data.InOutPoint.EndPoint; + Assert.False(rb1.EqualsButNoRomByte(rb2)); + Assert.False(rb1.Equals(rb2)); + } + + [Fact] + public void TestEquals() + { + var rb1 = SampleRomByte1(); + var rb2 = SampleRomByte1(); + + Assert.True(rb1.Equals(rb2)); + Assert.True(rb1.EqualsButNoRomByte(rb2)); + } + } +} diff --git a/Diz.Test/SerializerDictionaryTest.cs b/Diz.Test/SerializerDictionaryTest.cs new file mode 100644 index 00000000..9cf36e14 --- /dev/null +++ b/Diz.Test/SerializerDictionaryTest.cs @@ -0,0 +1,91 @@ +using System.Xml; +using Diz.Core.model; +using Diz.Core.serialization.xml_serializer; +using ExtendedXmlSerializer; +using ExtendedXmlSerializer.Configuration; +using IX.Observable; +using Xunit; +using Xunit.Abstractions; + +namespace Diz.Test +{ + public class SerializerDictionaryTest + { + private readonly ITestOutputHelper testOutputHelper; + + public SerializerDictionaryTest(ITestOutputHelper testOutputHelper) + { + this.testOutputHelper = testOutputHelper; + } + + public class TestRoot + { + public ObservableDictionary ODW { get; set; } = new ObservableDictionary() { + {1, "Z test1"}, + {2, "Z test3"}, + }; + public ObservableDictionary ODW2 { get; set; } = new ObservableDictionary() { + {100, new Label {Comment = "c1", Name = "location1"}}, + {200, new Label {Comment = "c2", Name = "location2"}}, + }; + + #region Equality + protected bool Equals(TestRoot other) + { + return + System.Linq.Enumerable.SequenceEqual(ODW, other.ODW) && + System.Linq.Enumerable.SequenceEqual(ODW2, other.ODW2); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((TestRoot)obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((ODW != null ? ODW.GetHashCode() : 0) * 397) ^ (ODW2 != null ? ODW2.GetHashCode() : 0); + } + } + #endregion + } + + private static IConfigurationContainer GetSerializer() + { + return XmlSerializerSupport.GetSerializer() + .EnableImplicitTyping(typeof(TestRoot)); + } + + [Fact] + private void Serializer() + { + var serializer = GetSerializer().Create(); + + var xmlStr = serializer.Serialize( + new XmlWriterSettings() {}, + testRootElementGood); + + testOutputHelper.WriteLine(xmlStr); + + Assert.Equal(xmlShouldBe, xmlStr); + } + + [Fact] + private void DeSerialize() + { + var serializer = GetSerializer().Create(); + var restoredRoot = serializer.Deserialize(xmlShouldBe); + + Assert.Equal(testRootElementGood, restoredRoot); + } + + private readonly TestRoot testRootElementGood = new TestRoot(); + + string xmlShouldBe = ""; + } +} \ No newline at end of file diff --git a/Diz.Test/app.config b/Diz.Test/app.config new file mode 100644 index 00000000..5df19485 --- /dev/null +++ b/Diz.Test/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Diz.Test/packages.config b/Diz.Test/packages.config new file mode 100644 index 00000000..b0f414c2 --- /dev/null +++ b/Diz.Test/packages.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DiztinGUIsh.sln b/DiztinGUIsh.sln index 60f3fa16..e9b78e65 100644 --- a/DiztinGUIsh.sln +++ b/DiztinGUIsh.sln @@ -1,10 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.421 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiztinGUIsh", "DiztinGUIsh\DiztinGUIsh.csproj", "{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diz.Core", "Diz.Core\Diz.Core.csproj", "{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diz.Test", "Diz.Test\Diz.Test.csproj", "{594DE30A-343F-4C09-828D-65A67626A491}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +19,14 @@ Global {2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Release|Any CPU.Build.0 = Release|Any CPU + {50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Release|Any CPU.Build.0 = Release|Any CPU + {594DE30A-343F-4C09-828D-65A67626A491}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {594DE30A-343F-4C09-828D-65A67626A491}.Debug|Any CPU.Build.0 = Debug|Any CPU + {594DE30A-343F-4C09-828D-65A67626A491}.Release|Any CPU.ActiveCfg = Release|Any CPU + {594DE30A-343F-4C09-828D-65A67626A491}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DiztinGUIsh.sln.DotSettings b/DiztinGUIsh.sln.DotSettings new file mode 100644 index 00000000..2c5807cc --- /dev/null +++ b/DiztinGUIsh.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/DiztinGUIsh/App.config b/DiztinGUIsh/App.config index 731f6de6..98dfa8bc 100644 --- a/DiztinGUIsh/App.config +++ b/DiztinGUIsh/App.config @@ -1,6 +1,37 @@ - + + + +
+ + - + - \ No newline at end of file + + + + + + + True + + + + + + + + + + + + + + + + + + + + diff --git a/DiztinGUIsh/BizHawkCdl.cs b/DiztinGUIsh/BizHawkCdl.cs deleted file mode 100644 index 00852dfb..00000000 --- a/DiztinGUIsh/BizHawkCdl.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace DiztinGUIsh -{ - public class BizHawkCdl : Dictionary> - { - [Flags] - public enum Flag : byte - { - None = 0x00, - ExecFirst = 0x01, - ExecOperand = 0x02, - CPUData = 0x04, - DMAData = 0x08, - CPUXFlag = 0x10, - CPUMFlag = 0x20, - BRR = 0x80 - } - - public static BizHawkCdl LoadFromFile(string path) - { - using (var fs = new FileStream(path, FileMode.Open)) - { - return LoadFromStream(fs); - } - } - - public static BizHawkCdl LoadFromStream(Stream input) - { - var br = new BinaryReader(input); - - string id = br.ReadString(); - string subType; - if (id == "BIZHAWK-CDL-1") - { - subType = "PCE"; - } - else if (id == "BIZHAWK-CDL-2") - { - subType = br.ReadString().TrimEnd(' '); - } - else - { - throw new InvalidDataException("File is not a BizHawk CDL file."); - } - - if (subType != "SNES") - { - throw new InvalidDataException("The CDL file is not for SNES."); - } - - var cdl = new BizHawkCdl(); - int count = br.ReadInt32(); - for (int i = 0; i < count; i++) - { - string key = br.ReadString(); - int len = br.ReadInt32(); - var data = br.ReadBytes(len).Select(b => (Flag)b).ToArray(); - cdl[key] = data; - } - - return cdl; - } - } -} diff --git a/DiztinGUIsh/DiztinGUIsh.csproj b/DiztinGUIsh/DiztinGUIsh.csproj index 0e227dc9..be72361a 100644 --- a/DiztinGUIsh/DiztinGUIsh.csproj +++ b/DiztinGUIsh/DiztinGUIsh.csproj @@ -8,10 +8,14 @@ WinExe DiztinGUIsh DiztinGUIsh - v4.6.1 + v4.8 512 true true + false + + + publish\ true Disk @@ -22,10 +26,10 @@ false false true - 0 + 1 1.0.0.%2a - false false + true true @@ -37,6 +41,7 @@ DEBUG;TRACE prompt 4 + 8.0 AnyCPU @@ -46,35 +51,146 @@ TRACE prompt 4 + 8.0 - resource\diz.ico + resource\diz-4.ico DiztinGUIsh.Program + + B5957BD4F7F637E843EB8B06E2C40D418498E46C + + + DiztinGUIsh_TemporaryKey.pfx + + + true + + + true + + + ..\packages\ByteSize.2.0.0\lib\net45\ByteSize.dll + + + ..\packages\FastBitmapLib.2.0.0\lib\net452\FastBitmapLib.dll + + + ..\packages\SharpZipLib.1.3.0\lib\net45\ICSharpCode.SharpZipLib.dll + + + ..\packages\IX.Abstractions.0.5.3\lib\net472\IX.Abstractions.dll + + + ..\packages\IX.Abstractions.Collections.0.5.3\lib\net472\IX.Abstractions.Collections.dll + + + ..\packages\IX.Abstractions.Threading.0.5.3\lib\net472\IX.Abstractions.Threading.dll + + + ..\packages\IX.Observable.0.5.3\lib\net472\IX.Observable.dll + + + ..\packages\IX.StandardExtensions.0.5.3\lib\net472\IX.StandardExtensions.dll + + + ..\packages\IX.StandardExtensions.ComponentModel.0.5.3\lib\net472\IX.StandardExtensions.ComponentModel.dll + + + ..\packages\IX.StandardExtensions.Threading.0.5.3\lib\net472\IX.StandardExtensions.Threading.dll + + + ..\packages\IX.Undoable.0.5.3\lib\net472\IX.Undoable.dll + + + ..\packages\JetBrains.Annotations.2020.1.0\lib\net20\JetBrains.Annotations.dll + + + ..\packages\LightInject.6.4.0\lib\net46\LightInject.dll + + + ..\packages\LiveCharts.0.9.7\lib\net45\LiveCharts.dll + + + ..\packages\LiveCharts.WinForms.0.9.7.1\lib\net45\LiveCharts.WinForms.dll + + + ..\packages\LiveCharts.Wpf.0.9.7\lib\net45\LiveCharts.Wpf.dll + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll + + - - - + + ..\packages\System.Interactive.4.1.1\lib\net45\System.Interactive.dll + + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + - + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + - + + + + + Form + + + BSNESTraceLogBinaryMonitorForm.cs + + + Form + + + + + Form About.cs - + + Form + + + ProgressDialog.cs + Form @@ -105,20 +221,71 @@ InOutPointChecker.cs - Form AliasList.cs + + + + + Form + + + + + Form + + + Form + + + Form + + + UserControl + + + BankLegend.cs + + + UserControl + + + BankLegendItem.cs + + + UserControl + + + RomBankVisualizer.cs + + + UserControl + + + RomFullVisualizer.cs + + + UserControl + + + RomImage.cs + + + Form + + + VisualizerForm.cs + Form MainWindow.cs - Form @@ -132,17 +299,19 @@ MisalignmentChecker.cs - - - - AliasList.cs About.cs + + BSNESTraceLogBinaryMonitorForm.cs + + + ProgressDialog.cs + ExportDisassembly.cs @@ -158,8 +327,27 @@ InOutPointChecker.cs + + BankLegend.cs + + + BankLegendItem.cs + + + RomBankVisualizer.cs + + + RomFullVisualizer.cs + + + RomImage.cs + + + VisualizerForm.cs + MainWindow.cs + Designer MarkManyDialog.cs @@ -175,8 +363,10 @@ True Resources.resx + True - + + SettingsSingleFileGenerator Settings.Designer.cs @@ -191,6 +381,10 @@ + + + + @@ -205,5 +399,21 @@ false + + + {50646b00-03d8-4cec-9ed1-3ef4cb199e8b} + Diz.Core + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/DiztinGUIsh/DiztinGUIsh.csproj.DotSettings b/DiztinGUIsh/DiztinGUIsh.csproj.DotSettings new file mode 100644 index 00000000..6e7fff86 --- /dev/null +++ b/DiztinGUIsh/DiztinGUIsh.csproj.DotSettings @@ -0,0 +1,2 @@ + + No \ No newline at end of file diff --git a/DiztinGUIsh/Program.cs b/DiztinGUIsh/Program.cs index 8a0954e1..d35856df 100644 --- a/DiztinGUIsh/Program.cs +++ b/DiztinGUIsh/Program.cs @@ -1,26 +1,39 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; +using DiztinGUIsh.window; namespace DiztinGUIsh { - static class Program + internal static class Program { - /// - /// The main entry point for the application. - /// + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern bool SetProcessDPIAware(); + [STAThread] static void Main(string[] args) { + var openFile = ""; + if (args.Length > 0) + openFile = args[0]; + + RunNormally(openFile); + } + + private static void RunNormally(string openFile = "") + { + if (Environment.OSVersion.Version.Major >= 6) + { + SetProcessDPIAware(); + } + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - MainWindow window = new MainWindow(); + var window = new MainWindow(); - if (args.Length > 0) window.openProject(args[0]); + if (openFile != "") + window.ProjectController.OpenProject(""); Application.Run(window); } } -} +} \ No newline at end of file diff --git a/DiztinGUIsh/Properties/Annotations.cs b/DiztinGUIsh/Properties/Annotations.cs new file mode 100644 index 00000000..1e718aae --- /dev/null +++ b/DiztinGUIsh/Properties/Annotations.cs @@ -0,0 +1,1240 @@ +// Note from Dom/Diztinguish: Jetbrains added this. Not sure we really need it, +// remove if you feel like it. probably won't hurt anything, just editor functionality. + +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; + +// ReSharper disable InheritdocConsiderUsage + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace DiztinGUIsh.Properties +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so checking for null is required before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element can never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + /// + /// public void Foo([ItemNotNull]List<string> books) + /// { + /// foreach (var book in books) { + /// if (book != null) // Warning: Expression is always true + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + /// + /// public void Foo([ItemCanBeNull]List<string> books) + /// { + /// foreach (var book in books) + /// { + /// // Warning: Possible 'System.NullReferenceException' + /// Console.WriteLine(book.ToUpper()); + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by the format pattern and (optional) arguments. + /// The parameter, which contains the format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as the format string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; } + } + + /// + /// Use this annotation to specify a type that contains static or const fields + /// with values for the annotated property/field/parameter. + /// The specified type will be used to improve completion suggestions. + /// + /// + /// namespace TestNamespace + /// { + /// public class Constants + /// { + /// public static int INT_CONST = 1; + /// public const string STRING_CONST = "1"; + /// } + /// + /// public class Class1 + /// { + /// [ValueProvider("TestNamespace.Constants")] public int myField; + /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } + /// + /// public void Test() + /// { + /// Foo(/*try completion here*/);// + /// myField = /*try completion here*/ + /// } + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + /// + /// Indicates that the integral value falls into the specified interval. + /// It's allowed to specify multiple non-intersecting intervals. + /// Values of interval boundaries are inclusive. + /// + /// + /// void Foo([ValueRange(0, 100)] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate, + AllowMultiple = true)] + public sealed class ValueRangeAttribute : Attribute + { + public object From { get; } + public object To { get; } + + public ValueRangeAttribute(long from, long to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(ulong from, ulong to) + { + From = from; + To = to; + } + + public ValueRangeAttribute(long value) + { + From = To = value; + } + + public ValueRangeAttribute(ulong value) + { + From = To = value; + } + } + + /// + /// Indicates that the integral value never falls below zero. + /// + /// + /// void Foo([NonNegativeValue] int value) { + /// if (value == -1) { // Warning: Expression is always 'false' + /// ... + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | + AttributeTargets.Method | AttributeTargets.Delegate)] + public sealed class NonNegativeValueAttribute : Attribute { } + + /// + /// Indicates that the function argument should be a string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If the method has a single input parameter, its name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for the method output + /// means that the method doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by the analysis engine.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("null <= param:null")] // reverse condition syntax + /// public string GetName(string surname) + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; } + + public bool ForceFullStates { get; } + } + + /// + /// Indicates whether the marked element should be localized. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be reported as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; } + + public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Can be applied to attributes, type parameters, and parameters of a type assignable from . + /// When applied to an attribute, the decorated attribute behaves the same as . + /// When applied to a type parameter or to a parameter of type , indicates that the corresponding type + /// is used implicitly. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Specify the details of implicitly used symbol when it is marked + /// with or . + /// + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered to be used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Inherited entities are considered used. + WithInheritors = 4, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + [AttributeUsage(AttributeTargets.All, Inherited = false)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Warning: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of the method invocation must be used. + /// + /// + /// Methods decorated with this attribute (in contrast to pure methods) might change state, + /// but make no sense without using their return value.
+ /// Similarly to , this attribute + /// will help detecting usages of the method when the return value in not used. + /// Additionally, you can optionally specify a custom message, which will be used when showing warnings, e.g. + /// [MustUseReturnValue("Use the return value to...")]. + ///
+ [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value of that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; } + } + + /// + /// An extension method marked with this attribute is processed by code completion + /// as a 'Source Template'. When the extension method is completed over some expression, its source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; } + } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + /// + /// Razor attribute. Indicates that the marked parameter or method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation, or property access + /// over collection type affects the contents of the collection. + /// Use to specify the access type. + /// + /// + /// Using this attribute only makes sense if all collection methods are marked with this attribute. + /// + /// + /// public class MyStringCollection : List<string> + /// { + /// [CollectionAccess(CollectionAccessType.Read)] + /// public string GetFirstString() + /// { + /// return this.ElementAt(0); + /// } + /// } + /// class Test + /// { + /// public void Foo() + /// { + /// // Warning: Contents of the collection is never updated + /// var col = new MyStringCollection(); + /// string x = col.GetFirstString(); + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; } + } + + /// + /// Provides a value for the to define + /// how the collection method invocation affects the contents of the collection. + /// + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts the control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable passed as a parameter is not enumerated. + /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. + /// + /// + /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class + /// { + /// // custom check for null but no enumeration + /// } + /// + /// void Foo(IEnumerable<string> values) + /// { + /// ThrowIfNull(values, nameof(values)); + /// var x = values.ToList(); // No warnings about multiple enumeration + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that the marked parameter is a regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns. + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some Style-derived type, that + /// is used to style items of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; } + + [NotNull] public Type ControlType { get; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; } + + [NotNull] public string FieldName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; } + [CanBeNull] public string PageName { get; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } +} \ No newline at end of file diff --git a/DiztinGUIsh/Properties/AssemblyInfo.cs b/DiztinGUIsh/Properties/AssemblyInfo.cs index 31234e5f..ae239a3b 100644 --- a/DiztinGUIsh/Properties/AssemblyInfo.cs +++ b/DiztinGUIsh/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -8,12 +7,23 @@ [assembly: AssemblyTitle("DiztinGUIsh")] [assembly: AssemblyDescription("A Super NES ROM disassembler.")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Written by Alex \"Dotsarecool\" Losego")] +[assembly: AssemblyCompany("Written by Alex \"Dotsarecool\" Losego, Dominic \"binary1230\" Cerquetti")] [assembly: AssemblyProduct("DiztinGUIsh")] -[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] +[assembly: AssemblyVersion( + ThisAssembly.Git.SemVer.Major + "." + + ThisAssembly.Git.SemVer.Minor + "." + + ThisAssembly.Git.SemVer.Patch)] +[assembly: AssemblyInformationalVersion( + ThisAssembly.Git.SemVer.Major + "." + + ThisAssembly.Git.SemVer.Minor + "." + + ThisAssembly.Git.SemVer.Patch + "-" + + ThisAssembly.Git.Branch + "+" + + ThisAssembly.Git.Commit)] + // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. @@ -32,5 +42,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.1.6")] -[assembly: AssemblyFileVersion("1.0.1.6")] +// [assembly: AssemblyVersion("1.0.1.6")] +// [assembly: AssemblyFileVersion("1.0.1.6")] diff --git a/DiztinGUIsh/Properties/DataSources/ROMByte.datasource b/DiztinGUIsh/Properties/DataSources/ROMByte.datasource deleted file mode 100644 index 2773c6d8..00000000 --- a/DiztinGUIsh/Properties/DataSources/ROMByte.datasource +++ /dev/null @@ -1,10 +0,0 @@ - - - - DiztinGUIsh.ROMByte, DiztinGUIsh, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - \ No newline at end of file diff --git a/DiztinGUIsh/Properties/Resources.Designer.cs b/DiztinGUIsh/Properties/Resources.Designer.cs index ea9fee9a..aabbe231 100644 --- a/DiztinGUIsh/Properties/Resources.Designer.cs +++ b/DiztinGUIsh/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace DiztinGUIsh.Properties -{ - - +namespace DiztinGUIsh.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,53 +19,85 @@ namespace DiztinGUIsh.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DiztinGUIsh.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap _1603231497loading_green { + get { + object obj = ResourceManager.GetObject("1603231497loading-green", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon diz { + get { + object obj = ResourceManager.GetObject("diz", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap diz_4 { + get { + object obj = ResourceManager.GetObject("diz_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon diz_41 { + get { + object obj = ResourceManager.GetObject("diz_41", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } } } diff --git a/DiztinGUIsh/Properties/Resources.resx b/DiztinGUIsh/Properties/Resources.resx index af7dbebb..b6c996d2 100644 --- a/DiztinGUIsh/Properties/Resources.resx +++ b/DiztinGUIsh/Properties/Resources.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Serialization.Formatters.Binary.BinaryFormatter + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,6 +60,7 @@ : and then encoded with base64 encoding. --> + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,9 +112,22 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resource\diz-4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\resource\diz.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\resource\diz-4.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\1603231497loading-green.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/DiztinGUIsh/Properties/Settings.Designer.cs b/DiztinGUIsh/Properties/Settings.Designer.cs index ef1c8d5b..23d8de68 100644 --- a/DiztinGUIsh/Properties/Settings.Designer.cs +++ b/DiztinGUIsh/Properties/Settings.Designer.cs @@ -8,23 +8,43 @@ // //------------------------------------------------------------------------------ -namespace DiztinGUIsh.Properties -{ - - +namespace DiztinGUIsh.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string LastOpenedFile { + get { + return ((string)(this["LastOpenedFile"])); + } + set { + this["LastOpenedFile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool OpenLastFileAutomatically { + get { + return ((bool)(this["OpenLastFileAutomatically"])); + } + set { + this["OpenLastFileAutomatically"] = value; + } + } } } diff --git a/DiztinGUIsh/Properties/Settings.settings b/DiztinGUIsh/Properties/Settings.settings index 39645652..22889e56 100644 --- a/DiztinGUIsh/Properties/Settings.settings +++ b/DiztinGUIsh/Properties/Settings.settings @@ -1,7 +1,12 @@  - - - - - - + + + + + + + + True + + + \ No newline at end of file diff --git a/DiztinGUIsh/ROMByte.cs b/DiztinGUIsh/ROMByte.cs deleted file mode 100644 index b1a9a25f..00000000 --- a/DiztinGUIsh/ROMByte.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DiztinGUIsh -{ - public class ROMByte - { - public byte Rom { get; set; } - public byte DataBank { get; set; } - public int DirectPage { get; set; } - public bool XFlag { get; set; } - public bool MFlag { get; set; } - public Data.FlagType TypeFlag { get; set; } - public Data.Architechture Arch { get; set; } - public Data.InOutPoint Point { get; set; } - } -} diff --git a/DiztinGUIsh/Resources/1603231497loading-green.gif b/DiztinGUIsh/Resources/1603231497loading-green.gif new file mode 100644 index 00000000..1fe03061 Binary files /dev/null and b/DiztinGUIsh/Resources/1603231497loading-green.gif differ diff --git a/DiztinGUIsh/controller/IProjectView.cs b/DiztinGUIsh/controller/IProjectView.cs new file mode 100644 index 00000000..a3b2fb85 --- /dev/null +++ b/DiztinGUIsh/controller/IProjectView.cs @@ -0,0 +1,22 @@ +using System; +using Diz.Core.export; +using Diz.Core.model; +using DiztinGUIsh.window.dialog; + +namespace DiztinGUIsh.controller +{ + public interface IProjectView + { + Project Project { get; set; } + void OnProjectOpenFail(string errorMsg); + void OnProjectSaved(); + void OnExportFinished(LogCreator.OutputResult result); + + public delegate void LongRunningTaskHandler(Action task, string description = null); + LongRunningTaskHandler TaskHandler { get; } + void SelectOffset(int offset, int column=-1); + string AskToSelectNewRomFilename(string promptSubject, string promptText); + IImportRomDialogView GetImportView(); + void OnProjectOpenWarning(string warningMsg); + } +} diff --git a/DiztinGUIsh/controller/ProjectController.cs b/DiztinGUIsh/controller/ProjectController.cs new file mode 100644 index 00000000..5c88fb6c --- /dev/null +++ b/DiztinGUIsh/controller/ProjectController.cs @@ -0,0 +1,284 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Diz.Core.export; +using Diz.Core.import; +using Diz.Core.model; +using Diz.Core.serialization; +using DiztinGUIsh.util; +using DiztinGUIsh.window.dialog; + +// Model-View-Controller architecture. +// goal: while this class's purpose is to be the middleman between dumb GUI elements and +// our underlying data, we should strive to keep all direct GUI stuff out of this class. +// i.e. it's OK for this class to deal with confirming the user wants to do something, +// but not OK to directly use GUI functions to show a form. instead, it should reach +// out to the view classes and ask them to do things like popup dialog boxes/change form elements/etc. +// +// The idea here is that because there's no direct GUI stuff going on, we can run automation +// and unit testing on this class, and eventually add undo/redo support +// +// Where possible, let the GUI elements (forms) subscribe to data notifications on our model +// instead of trying to middleman everything. +// +// This separation of concerns isn't perfect yet, but, keep it in mind as you add functionality. +// +// example: +// ProjectView -> A form that displays an opened project to the user +// ProjectController -> When the form needs to change any state, it talks to ProjectController +// i.e. when user clicks "Open Project", it sends the filename to us for handling +// Project -> The actual data, the model. It knows nothing about GUI, just is the low-level business logic + +namespace DiztinGUIsh.controller +{ + public class ProjectController + { + public IProjectView ProjectView { get; set; } + public Project Project { get; private set; } + + public delegate void ProjectChangedEvent(object sender, ProjectChangedEventArgs e); + + public event ProjectChangedEvent ProjectChanged; + + public class ProjectChangedEventArgs + { + public enum ProjectChangedType + { + Invalid, + Saved, + Opened, + Imported, + Closing + } + + public ProjectChangedType ChangeType; + public Project Project; + public string Filename; + } + + // there's probably better ways to handle this. + // probably replace with a UI like "start task" and "stop task" + // so we can flip up a progress bar and remove it. + public void DoLongRunningTask(Action task, string description = null) + { + if (ProjectView.TaskHandler != null) + ProjectView.TaskHandler(task, description); + else + task(); + } + + public bool OpenProject(string filename) + { + Project project = null; + var errorMsg = ""; + var warningMsg = ""; + + DoLongRunningTask(delegate + { + try + { + var (project1, warning) = new ProjectFileManager() + { + RomPromptFn = AskToSelectNewRomFilename + }.Open(filename); + + project = project1; + warningMsg = warning; + } + catch (AggregateException ex) + { + project = null; + errorMsg = ex.InnerExceptions.Select(e => e.Message).Aggregate((line, val) => line += val + "\n"); + } + catch (Exception ex) + { + project = null; + errorMsg = ex.Message; + } + }, $"Opening {Path.GetFileName(filename)}..."); + + if (project == null) + { + ProjectView.OnProjectOpenFail(errorMsg); + return false; + } + + if (warningMsg != "") + ProjectView.OnProjectOpenWarning(warningMsg); + + OnProjectOpenSuccess(filename, project); + return true; + } + + private void OnProjectOpenSuccess(string filename, Project project) + { + ProjectView.Project = Project = project; + Project.PropertyChanged += Project_PropertyChanged; + + ProjectChanged?.Invoke(this, new ProjectChangedEventArgs() + { + ChangeType = ProjectChangedEventArgs.ProjectChangedType.Opened, + Filename = filename, + Project = project, + }); + } + + private void Project_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + // TODO: use this to listen to interesting change events in Project/Data + // so we can react appropriately. + } + + public void SaveProject(string filename) + { + DoLongRunningTask(delegate { new ProjectFileManager().Save(Project, filename); }, + $"Saving {Path.GetFileName(filename)}..."); + ProjectView.OnProjectSaved(); + } + + public void ImportBizHawkCdl(string filename) + { + BizHawkCdlImporter.Import(filename, Project.Data); + + ProjectChanged?.Invoke(this, new ProjectChangedEventArgs() + { + ChangeType = ProjectChangedEventArgs.ProjectChangedType.Imported, + Filename = filename, + Project = Project, + }); + } + + public bool ImportRomAndCreateNewProject(string romFilename) + { + // let the user select settings on the GUI + var importController = new ImportRomDialogController {View = ProjectView.GetImportView()}; + importController.View.Controller = importController; + var importSettings = importController.PromptUserForRomSettings(romFilename); + if (importSettings == null) + return false; + + CloseProject(); + + // actually do the import + ImportRomAndCreateNewProject(importSettings); + return true; + } + + public void ImportRomAndCreateNewProject(ImportRomSettings importSettings) + { + var project = BaseProjectFileManager.ImportRomAndCreateNewProject(importSettings); + OnProjectOpenSuccess(project.ProjectFileName, project); + } + + private string AskToSelectNewRomFilename(string error) + { + return ProjectView.AskToSelectNewRomFilename("Error", $"{error} Link a new ROM now?"); + } + + public void WriteAssemblyOutput() + { + WriteAssemblyOutput(Project.LogWriterSettings, true); + } + + private void WriteAssemblyOutput(LogWriterSettings settings, bool showProgressBarUpdates = false) + { + var lc = new LogCreator() + { + Settings = settings, + Data = Project.Data, + }; + + LogCreator.OutputResult result = null; + DoLongRunningTask(delegate { result = lc.CreateLog(); }, "Exporting assembly source code..."); + + ProjectView.OnExportFinished(result); + } + + public void UpdateExportSettings(LogWriterSettings selectedSettings) + { + // TODO: ref readonly or similar here, to save us an extra copy of the struct? + + Project.LogWriterSettings = selectedSettings; + } + + public void MarkChanged() + { + // eventually set this via INotifyPropertyChanged or similar. + Project.UnsavedChanges = true; + } + + public void SelectOffset(int offset, int column = -1) + { + ProjectView.SelectOffset(offset, column); + } + + public long ImportBsnesUsageMap(string fileName) + { + var importer = new BsnesUsageMapImporter(); + + var linesModified = importer.ImportUsageMap(File.ReadAllBytes(fileName), Project.Data); + + if (linesModified > 0) + MarkChanged(); + + return linesModified; + } + + public long ImportBsnesTraceLogs(string[] fileNames) + { + var importer = new BsnesTraceLogImporter(Project.Data); + + // TODO: differentiate between binary-formatted and text-formatted files + // probably look for a newline within 80 characters + // call importer.ImportTraceLogLineBinary() + + // caution: trace logs can be gigantic, even a few seconds can be > 1GB + // inside here, performance becomes critical. + LargeFilesReader.ReadFilesLines(fileNames, + (line) => + { + importer.ImportTraceLogLine(line); + }); + + if (importer.CurrentStats.NumRomBytesModified > 0) + MarkChanged(); + + return importer.CurrentStats.NumRomBytesModified; + } + + public long ImportBsnesTraceLogsBinary(string[] filenames) + { + var importer = new BsnesTraceLogImporter(Project.Data); + + foreach (var file in filenames) + { + using Stream source = File.OpenRead(file); + const int bytesPerPacket = 22; + var buffer = new byte[bytesPerPacket]; + int bytesRead; + while ((bytesRead = source.Read(buffer, 0, bytesPerPacket)) > 0) + { + Debug.Assert(bytesRead == 22); + importer.ImportTraceLogLineBinary(buffer); + } + } + + return importer.CurrentStats.NumRomBytesModified; + } + + public void CloseProject() + { + if (Project == null) + return; + + ProjectChanged?.Invoke(this, new ProjectChangedEventArgs() + { + ChangeType = ProjectChangedEventArgs.ProjectChangedType.Closing + }); + + Project = null; + } + } +} diff --git a/DiztinGUIsh/diz-4.ico b/DiztinGUIsh/diz-4.ico new file mode 100644 index 00000000..e9c5163e Binary files /dev/null and b/DiztinGUIsh/diz-4.ico differ diff --git a/DiztinGUIsh/licensing/Library Licenses.dist.txt b/DiztinGUIsh/licensing/Library Licenses.dist.txt new file mode 100644 index 00000000..1eb3460b --- /dev/null +++ b/DiztinGUIsh/licensing/Library Licenses.dist.txt @@ -0,0 +1,61 @@ +Castle.Core http://www.apache.org/licenses/LICENSE-2.0.html Apache 2.0 +xunit https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +xunit.abstractions https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +xunit.analyzers https://raw.githubusercontent.com/xunit/xunit.analyzers/master/LICENSE Apache 2.0 +xunit.assert https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +xunit.core https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +xunit.extensibility.core https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +xunit.extensibility.execution https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +xunit.runner.console https://raw.githubusercontent.com/xunit/xunit/master/license.txt Apache 2.0 +Moq https://raw.githubusercontent.com/moq/moq4/master/License.txt BSD 3clause +ByteSize https://github.com/omar/ByteSize/blob/master/LICENSE MIT +ExtendedXmlSerializer https://github.com/ExtendedXmlSerializer/home/blob/master/LICENSE MIT +FastBitmapLib https://licenses.nuget.org/MIT MIT +GitInfo https://licenses.nuget.org/MIT MIT +IX.Abstractions https://licenses.nuget.org/MIT MIT +IX.Abstractions.Collections https://licenses.nuget.org/MIT MIT +IX.Abstractions.Threading https://licenses.nuget.org/MIT MIT +IX.Observable https://licenses.nuget.org/MIT MIT +IX.StandardExtensions https://licenses.nuget.org/MIT MIT +IX.StandardExtensions.ComponentModel https://licenses.nuget.org/MIT MIT +IX.StandardExtensions.Threading https://licenses.nuget.org/MIT MIT +IX.Undoable https://licenses.nuget.org/MIT MIT +JetBrains.Annotations https://licenses.nuget.org/MIT MIT +LightInject https://licenses.nuget.org/MIT MIT +LiveCharts https://github.com/beto-rodriguez/Live-Charts/blob/master/LICENSE.TXT MIT +NReco.LambdaParser https://raw.githubusercontent.com/nreco/lambdaparser/master/LICENSE MIT +SharpZipLib https://licenses.nuget.org/MIT MIT +Sprache https://github.com/sprache/Sprache/blob/master/licence.txt MIT +xunit.runner.visualstudio https://licenses.nuget.org/MIT MIT +-------------------------------------------------- +"MOQ +BSD 3-Clause License + +Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, +and Contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +-------------------------------------------------- diff --git a/DiztinGUIsh/licensing/Library Licenses.xlsx b/DiztinGUIsh/licensing/Library Licenses.xlsx new file mode 100644 index 00000000..5655cb9e Binary files /dev/null and b/DiztinGUIsh/licensing/Library Licenses.xlsx differ diff --git a/DiztinGUIsh/packages.config b/DiztinGUIsh/packages.config new file mode 100644 index 00000000..d3ec5c74 --- /dev/null +++ b/DiztinGUIsh/packages.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DiztinGUIsh/packages.config.new.20201015004634 b/DiztinGUIsh/packages.config.new.20201015004634 new file mode 100644 index 00000000..f3ad379b --- /dev/null +++ b/DiztinGUIsh/packages.config.new.20201015004634 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DiztinGUIsh/resource/diz-4.ico b/DiztinGUIsh/resource/diz-4.ico new file mode 100644 index 00000000..e9c5163e Binary files /dev/null and b/DiztinGUIsh/resource/diz-4.ico differ diff --git a/DiztinGUIsh/resource/diz-4.png b/DiztinGUIsh/resource/diz-4.png new file mode 100644 index 00000000..a904f4d5 Binary files /dev/null and b/DiztinGUIsh/resource/diz-4.png differ diff --git a/DiztinGUIsh/static/Data.cs b/DiztinGUIsh/static/Data.cs deleted file mode 100644 index 29bf2ab8..00000000 --- a/DiztinGUIsh/static/Data.cs +++ /dev/null @@ -1,306 +0,0 @@ -using DiztinGUIsh.window; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DiztinGUIsh -{ - public static class Data - { - public enum FlagType : byte - { - Unreached = 0x00, - Opcode = 0x10, - Operand = 0x11, - Data8Bit = 0x20, - Graphics = 0x21, - Music = 0x22, - Empty = 0x23, - Data16Bit = 0x30, - Pointer16Bit = 0x31, - Data24Bit = 0x40, - Pointer24Bit = 0x41, - Data32Bit = 0x50, - Pointer32Bit = 0x51, - Text = 0x60 - } - - public enum Architechture : byte - { - CPU65C816 = 0x00, - APUSPC700 = 0x01, - GPUSuperFX = 0x02 - } - - [Flags] - public enum InOutPoint : byte - { - InPoint = 0x01, - OutPoint = 0x02, - EndPoint = 0x04, - ReadPoint = 0x08 - } - - public enum ROMMapMode : byte - { - LoROM, HiROM, ExHiROM, SA1ROM, ExSA1ROM, SuperFX, SuperMMC, ExLoROM - } - - public enum ROMSpeed : byte - { - SlowROM, FastROM, Unknown - } - - public const int - LOROM_SETTING_OFFSET = 0x7FD5, - HIROM_SETTING_OFFSET = 0xFFD5, - EXHIROM_SETTING_OFFSET = 0x40FFD5, - EXLOROM_SETTING_OFFSET = 0x407FD5; - - private static ROMMapMode rom_map_mode; - private static ROMSpeed rom_speed; - private static List table; - private static Dictionary comment; - - public class AliasInfo - { - public string name = ""; // name of the label - public string comment = ""; // user-generated text, comment only - - public void CleanUp() - { - if (comment == null) comment = ""; - if (name == null) name = ""; - } - } - private static Dictionary alias; - - public static void Initiate(byte[] data, ROMMapMode mode, ROMSpeed speed) - { - rom_map_mode = mode; - rom_speed = speed; - int size = data.Length; - alias = new Dictionary(); - comment = new Dictionary(); - table = new List(); - for (int i = 0; i < size; i++) - { - ROMByte r = new ROMByte - { - Rom = data[i], - DataBank = 0, - DirectPage = 0, - XFlag = false, - MFlag = false, - TypeFlag = FlagType.Unreached, - Arch = Architechture.CPU65C816, - Point = 0 - }; - table.Add(r); - } - } - - public static void Restore(List l = null, ROMMapMode m = ROMMapMode.LoROM, ROMSpeed s = ROMSpeed.Unknown, Dictionary a = null, Dictionary c = null) - { - table = l ?? table; - rom_map_mode = s == ROMSpeed.Unknown ? rom_map_mode : m; - rom_speed = s == ROMSpeed.Unknown ? rom_speed : s; - alias = a ?? alias; - comment = c ?? comment; - } - - public static ROMMapMode GetROMMapMode() - { - return rom_map_mode; - } - - public static ROMSpeed GetROMSpeed() - { - return rom_speed; - } - - public static List GetTable() - { - return table; - } - - public static int GetROMByte(int i) - { - return table[i].Rom; - } - - public static int GetROMSize() - { - return table == null ? 0 : table.Count; - } - - public static FlagType GetFlag(int i) - { - return table[i].TypeFlag; - } - - public static void SetFlag(int i, FlagType flag) - { - table[i].TypeFlag = flag; - } - - public static Architechture GetArchitechture(int i) - { - return table[i].Arch; - } - - public static void SetArchitechture(int i, Architechture arch) - { - table[i].Arch = arch; - } - - public static InOutPoint GetInOutPoint(int i) - { - return table[i].Point; - } - - public static void SetInOutPoint(int i, InOutPoint point) - { - table[i].Point |= point; - } - - public static void ClearInOutPoint(int i) - { - table[i].Point = 0; - } - - public static int GetDataBank(int i) - { - return table[i].DataBank; - } - - public static void SetDataBank(int i, int dbank) - { - table[i].DataBank = (byte)dbank; - } - - public static int GetDirectPage(int i) - { - return table[i].DirectPage; - } - - public static void SetDirectPage(int i, int dpage) - { - table[i].DirectPage = 0xFFFF & dpage; - } - - public static bool GetXFlag(int i) - { - return table[i].XFlag; - } - - public static void SetXFlag(int i, bool x) - { - table[i].XFlag = x; - } - - public static bool GetMFlag(int i) - { - return table[i].MFlag; - } - - public static void SetMFlag(int i, bool m) - { - table[i].MFlag = m; - } - - public static int GetMXFlags(int i) - { - return (table[i].MFlag ? 0x20 : 0) | (table[i].XFlag ? 0x10 : 0); - } - - public static void SetMXFlags(int i, int mx) - { - table[i].MFlag = ((mx & 0x20) != 0); - table[i].XFlag = ((mx & 0x10) != 0); - } - - public static string GetLabelName(int i) - { - if (alias.TryGetValue(i, out AliasInfo val)) - return val?.name ?? ""; - - return ""; - } - public static string GetLabelComment(int i) - { - if (alias.TryGetValue(i, out AliasInfo val)) - return val?.comment ?? ""; - - return ""; - } - - public static void AddLabel(int i, AliasInfo v, bool overwrite) - { - if (v == null) - { - if (alias.ContainsKey(i)) - { - alias.Remove(i); - AliasList.me.RemoveRow(i); - } - } else { - if (alias.ContainsKey(i) && overwrite) - { - alias.Remove(i); - AliasList.me.RemoveRow(i); - } - if (!alias.ContainsKey(i)) - { - v.CleanUp(); - - alias.Add(i, v); - AliasList.me.AddRow(i, v); - } - } - } - - public static Dictionary GetAllLabels() - { - return alias; - } - - public static string GetComment(int i) - { - if (comment.TryGetValue(i, out string val)) return val; - return ""; - } - - public static void AddComment(int i, string v, bool overwrite) - { - if (v == null) - { - if (comment.ContainsKey(i)) comment.Remove(i); - } else - { - if (comment.ContainsKey(i) && overwrite) comment.Remove(i); - if (!comment.ContainsKey(i)) comment.Add(i, v); - } - } - - public static Dictionary GetAllComments() - { - return comment; - } - - public static int GetRomSettingOffset(ROMMapMode mode) - { - switch (mode) - { - case ROMMapMode.LoROM: return LOROM_SETTING_OFFSET; - case ROMMapMode.HiROM: return HIROM_SETTING_OFFSET; - case ROMMapMode.ExHiROM: return EXHIROM_SETTING_OFFSET; - case ROMMapMode.ExLoROM: return EXLOROM_SETTING_OFFSET; - } - return LOROM_SETTING_OFFSET; - } - } -} diff --git a/DiztinGUIsh/static/LogCreator.cs b/DiztinGUIsh/static/LogCreator.cs deleted file mode 100644 index 03180b28..00000000 --- a/DiztinGUIsh/static/LogCreator.cs +++ /dev/null @@ -1,542 +0,0 @@ -using DiztinGUIsh.window; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DiztinGUIsh -{ - public static class LogCreator - { - public enum FormatUnlabeled - { - ShowAll = 0, - ShowInPoints = 1, // TODO Add Show In Points with +/- labels - ShowNone = 2 - } - - public enum FormatStructure - { - SingleFile = 0, - OneBankPerFile = 1 - } - - public static Dictionary, int>> parameters = new Dictionary, int>> - { - { "", Tuple.Create, int>(GetPercent, 1) }, - { "label", Tuple.Create, int>(GetLabel, -22) }, - { "code", Tuple.Create, int>(GetCode, 37) }, - { "ia", Tuple.Create, int>(GetIntermediateAddress, 6) }, - { "pc", Tuple.Create, int>(GetProgramCounter, 6) }, - { "offset", Tuple.Create, int>(GetOffset, -6) }, - { "bytes", Tuple.Create, int>(GetRawBytes, 8) }, - { "comment", Tuple.Create, int>(GetComment, 1) }, - { "b", Tuple.Create, int>(GetDataBank, 2) }, - { "d", Tuple.Create, int>(GetDirectPage, 4) }, - { "m", Tuple.Create, int>(GetMFlag, 1) }, - { "x", Tuple.Create, int>(GetXFlag, 1) }, - { "%org", Tuple.Create, int>(GetORG, 37) }, - { "%map", Tuple.Create, int>(GetMap, 37) }, - { "%empty", Tuple.Create, int>(GetEmpty, 1) }, - { "%incsrc", Tuple.Create, int>(GetIncSrc, 1) }, - { "%bankcross", Tuple.Create, int>(GetBankCross, 1) }, - { "%labelassign", Tuple.Create, int>(GetLabelAssign, 1) }, - }; - - public static string format = "%label:-22% %code:37%;%pc%|%bytes%|%ia%; %comment%"; - public static int dataPerLine = 8; - public static FormatUnlabeled unlabeled = FormatUnlabeled.ShowInPoints; - public static FormatStructure structure = FormatStructure.OneBankPerFile; - public static bool includeUnusedLabels; - public static bool printLabelSpecificComments; - - private static List> list; - private static List usedLabels; - private static StreamWriter err; - private static int errorCount, bankSize; - private static string folder; - - public static int CreateLog(StreamWriter sw, StreamWriter er) - { - var tempAlias = Data.GetAllLabels(); - Data.Restore(a: new Dictionary(tempAlias)); - AliasList.me.locked = true; - bankSize = Data.GetROMMapMode() == Data.ROMMapMode.LoROM ? 0x8000 : 0x10000; // todo - - AddTemporaryLabels(); - - string[] split = format.Split('%'); - err = er; - errorCount = 0; - usedLabels = new List(); - - list = new List>(); - for (int i = 0; i < split.Length; i++) - { - if (i % 2 == 0) list.Add(Tuple.Create(split[i], int.MaxValue)); - else - { - int colon = split[i].IndexOf(':'); - if (colon < 0) list.Add(Tuple.Create(split[i], parameters[split[i]].Item2)); - else list.Add(Tuple.Create(split[i].Substring(0, colon), int.Parse(split[i].Substring(colon + 1)))); - } - } - - int pointer = 0, size = (Data.GetTable() == ExportDisassembly.sampleTable) ? 0x7B : Data.GetROMSize(), bank = -1; - - if (structure == FormatStructure.OneBankPerFile) - { - folder = Path.GetDirectoryName(((FileStream)sw.BaseStream).Name); - sw.WriteLine(GetLine(pointer, "map")); - sw.WriteLine(GetLine(pointer, "empty")); - for (int i = 0; i < size; i += bankSize) sw.WriteLine(GetLine(i, "incsrc")); - sw.WriteLine(GetLine(-1, "incsrc")); - } else - { - sw.WriteLine(GetLine(pointer, "map")); - sw.WriteLine(GetLine(pointer, "empty")); - } - - while (pointer < size) - { - int snes = Util.ConvertPCtoSNES(pointer); - if ((snes >> 16) != bank) - { - if (structure == FormatStructure.OneBankPerFile) - { - sw.Close(); - sw = new StreamWriter(string.Format("{0}/bank_{1}.asm", folder, Util.NumberToBaseString((snes >> 16), Util.NumberBase.Hexadecimal, 2))); - } - - sw.WriteLine(GetLine(pointer, "empty")); - sw.WriteLine(GetLine(pointer, "org")); - sw.WriteLine(GetLine(pointer, "empty")); - if ((snes % bankSize) != 0) err.WriteLine("({0}) Offset 0x{1:X}: An instruction crossed a bank boundary.", ++errorCount, pointer); - bank = snes >> 16; - } - - var c1 = (Data.GetInOutPoint(pointer) & (Data.InOutPoint.ReadPoint)) != 0; - var c2 = (tempAlias.TryGetValue(pointer, out var aliasInfo) && aliasInfo.name.Length > 0); - if (c1 || c2) - sw.WriteLine(GetLine(pointer, "empty")); - - sw.WriteLine(GetLine(pointer, null)); - if ((Data.GetInOutPoint(pointer) & (Data.InOutPoint.EndPoint)) != 0) sw.WriteLine(GetLine(pointer, "empty")); - pointer += GetLineByteLength(pointer); - } - - WriteLabels(ref sw, pointer); - - if (structure == FormatStructure.OneBankPerFile) sw.Close(); - Data.Restore(a: tempAlias); - AliasList.me.locked = false; - return errorCount; - } - - private static void WriteLabels(ref StreamWriter sw, int pointer) - { - SwitchOutputFile(ref sw, pointer, $"{folder}/labels.asm"); - - Dictionary listToPrint = new Dictionary(); - - // part 1: important: include all labels we aren't defining somewhere else. needed for disassembly - foreach (var pair in Data.GetAllLabels()) - { - if (usedLabels.Contains(pair.Key)) - continue; - - // this label was not defined elsewhere in our disassembly, so we need to include it in labels.asm - listToPrint.Add(pair.Key, pair.Value); - } - - foreach (var pair in listToPrint) - { - sw.WriteLine(GetLine(pair.Key, "labelassign")); - } - - // part 2: optional: if requested, print all labels regardless of use. - // Useful for debugging, documentation, or reverse engineering workflow. - // this file shouldn't need to be included in the build, it's just reference documentation - if (includeUnusedLabels) - { - SwitchOutputFile(ref sw, pointer, $"{folder}/all-labels.txt"); - foreach (var pair in Data.GetAllLabels()) - { - // not the best place to add formatting, TODO: cleanup - var category = listToPrint.ContainsKey(pair.Key) ? "INLINE" : "EXTRA "; - sw.WriteLine($";!^!-{category}-! " + GetLine(pair.Key, "labelassign")); - } - } - } - - private static void SwitchOutputFile(ref StreamWriter sw, int pointer, string path) - { - if (structure == FormatStructure.OneBankPerFile) - { - sw.Close(); - sw = new StreamWriter(path); - } - else - { - sw.WriteLine(GetLine(pointer, "empty")); - } - } - - private static void AddTemporaryLabels() - { - List addMe = new List(); - int pointer = 0; - - while (pointer < Data.GetROMSize()) - { - int length = GetLineByteLength(pointer); - Data.FlagType flag = Data.GetFlag(pointer); - - if (unlabeled == FormatUnlabeled.ShowAll) addMe.Add(Util.ConvertPCtoSNES(pointer)); - else if (unlabeled != FormatUnlabeled.ShowNone && - (flag == Data.FlagType.Opcode || flag == Data.FlagType.Pointer16Bit || flag == Data.FlagType.Pointer24Bit || flag == Data.FlagType.Pointer32Bit)) - { - int ia = Util.GetIntermediateAddressOrPointer(pointer); - if (ia >= 0 && Util.ConvertSNEStoPC(ia) >= 0) addMe.Add(ia); - } - - pointer += length; - } - - // TODO +/- labels - for (int i = 0; i < addMe.Count; i++) - { - Data.AddLabel(addMe[i], - new Data.AliasInfo() - { - name = Util.GetDefaultLabel(addMe[i]) - }, - false); - } - } - - private static string GetLine(int offset, string special) - { - string line = ""; - for (int i = 0; i < list.Count; i++) - { - if (list[i].Item2 == int.MaxValue) // string literal - { - line += list[i].Item1; - } - else if (special != null) // special parameter (replaces code & everything else = empty) - { - line += GetParameter(offset, "%" + (list[i].Item1 == "code" ? special : "empty"), list[i].Item2); - } - else // normal parameter - { - line += GetParameter(offset, list[i].Item1, list[i].Item2); - } - } - - if (special == null) - { - // throw out some errors if stuff looks fishy - Data.FlagType flag = Data.GetFlag(offset), check = flag == Data.FlagType.Opcode ? Data.FlagType.Operand : flag; - int step = flag == Data.FlagType.Opcode ? GetLineByteLength(offset) : Util.TypeStepSize(flag), size = Data.GetROMSize(); - if (flag == Data.FlagType.Operand) err.WriteLine("({0}) Offset 0x{1:X}: Bytes marked as operands formatted as data.", ++errorCount, offset); - else if (step > 1) - { - for (int i = 1; i < step; i++) - { - if (offset + i >= size) - { - err.WriteLine("({0}) Offset 0x{1:X}: {2} extends past the end of the ROM.", ++errorCount, offset, Util.TypeToString(check)); - break; - } - else if (Data.GetFlag(offset + i) != check) - { - err.WriteLine("({0}) Offset 0x{1:X}: Expected {2}, but got {3} instead.", ++errorCount, offset + i, Util.TypeToString(check), Util.TypeToString(Data.GetFlag(offset + i))); - break; - } - } - } - int ia = Util.GetIntermediateAddress(offset); - if (ia >= 0 && flag == Data.FlagType.Opcode && Data.GetInOutPoint(offset) == Data.InOutPoint.OutPoint && Data.GetFlag(Util.ConvertSNEStoPC(ia)) != Data.FlagType.Opcode) - { - err.WriteLine("({0}) Offset 0x{1:X}: Branch or jump instruction to a non-instruction.", ++errorCount, offset); - } - } - - return line; - } - - private static int GetLineByteLength(int offset) - { - int max = 1, step = 1; - int size = Data.GetROMSize(); - - switch (Data.GetFlag(offset)) - { - case Data.FlagType.Opcode: - switch (Data.GetArchitechture(offset)) - { - case Data.Architechture.CPU65C816: return CPU65C816.GetInstructionLength(offset); - case Data.Architechture.APUSPC700: return 1; - case Data.Architechture.GPUSuperFX: return 1; - } - return 1; - case Data.FlagType.Unreached: - case Data.FlagType.Operand: - case Data.FlagType.Data8Bit: - case Data.FlagType.Graphics: - case Data.FlagType.Music: - case Data.FlagType.Empty: - max = dataPerLine; - break; - case Data.FlagType.Text: - max = 21; - break; - case Data.FlagType.Data16Bit: - step = 2; - max = dataPerLine; - break; - case Data.FlagType.Data24Bit: - step = 3; - max = dataPerLine; - break; - case Data.FlagType.Data32Bit: - step = 4; - max = dataPerLine; - break; - case Data.FlagType.Pointer16Bit: - step = 2; - max = 2; - break; - case Data.FlagType.Pointer24Bit: - step = 3; - max = 3; - break; - case Data.FlagType.Pointer32Bit: - step = 4; - max = 4; - break; - } - - int min = step, myBank = offset / bankSize; - while ( - min < max && - offset + min < size && - Data.GetFlag(offset + min) == Data.GetFlag(offset) && - Data.GetLabelName(Util.ConvertPCtoSNES(offset + min)) == "" && - (offset + min) / bankSize == myBank - ) min += step; - return min; - } - - public static string GetParameter(int offset, string parameter, int length) - { - if (parameters.TryGetValue(parameter, out Tuple, int> tup)) return tup.Item1.Invoke(offset, length); - return ""; - } - - // just a % - private static string GetPercent(int offset, int length) - { - return "%"; - } - - // all spaces - private static string GetEmpty(int offset, int length) - { - return string.Format("{0," + length + "}", ""); - } - - // trim to length - // negative length = right justified - private static string GetLabel(int offset, int length) - { - int snes = Util.ConvertPCtoSNES(offset); - string label = Data.GetLabelName(snes); - if (label == null) - return ""; - - usedLabels.Add(snes); - bool noColon = label.Length == 0 || label[0] == '-' || label[0] == '+'; - return string.Format("{0," + (length * -1) + "}", label + (noColon ? "" : ":")); - } - - // trim to length - private static string GetCode(int offset, int length) - { - int bytes = GetLineByteLength(offset); - string code = ""; - - switch (Data.GetFlag(offset)) - { - case Data.FlagType.Opcode: - code = Util.GetInstruction(offset); - break; - case Data.FlagType.Unreached: - case Data.FlagType.Operand: - case Data.FlagType.Data8Bit: - case Data.FlagType.Graphics: - case Data.FlagType.Music: - case Data.FlagType.Empty: - code = Util.GetFormattedBytes(offset, 1, bytes); - break; - case Data.FlagType.Data16Bit: - code = Util.GetFormattedBytes(offset, 2, bytes); - break; - case Data.FlagType.Data24Bit: - code = Util.GetFormattedBytes(offset, 3, bytes); - break; - case Data.FlagType.Data32Bit: - code = Util.GetFormattedBytes(offset, 4, bytes); - break; - case Data.FlagType.Pointer16Bit: - code = Util.GetPointer(offset, 2); - break; - case Data.FlagType.Pointer24Bit: - code = Util.GetPointer(offset, 3); - break; - case Data.FlagType.Pointer32Bit: - code = Util.GetPointer(offset, 4); - break; - case Data.FlagType.Text: - code = Util.GetFormattedText(offset, bytes); - break; - } - - return string.Format("{0," + (length * -1) + "}", code); - } - - private static string GetORG(int offset, int length) - { - string org = "ORG " + Util.NumberToBaseString(Util.ConvertPCtoSNES(offset), Util.NumberBase.Hexadecimal, 6, true); - return string.Format("{0," + (length * -1) + "}", org); - } - - private static string GetMap(int offset, int length) - { - string s = ""; - switch (Data.GetROMMapMode()) - { - case Data.ROMMapMode.LoROM: s = "lorom"; break; - case Data.ROMMapMode.HiROM: s = "hirom"; break; - case Data.ROMMapMode.SA1ROM: s = "sa1rom"; break; // todo - case Data.ROMMapMode.ExSA1ROM: s = "exsa1rom"; break; // todo - case Data.ROMMapMode.SuperFX: s = "sfxrom"; break; // todo - case Data.ROMMapMode.ExHiROM: s = "exhirom"; break; - case Data.ROMMapMode.ExLoROM: s = "exlorom"; break; - } - return string.Format("{0," + (length * -1) + "}", s); - } - - // 0+ = bank_xx.asm, -1 = labels.asm - private static string GetIncSrc(int offset, int length) - { - string s = "incsrc \"labels.asm\""; - if (offset >= 0) - { - int bank = Util.ConvertPCtoSNES(offset) >> 16; - s = string.Format("incsrc \"bank_{0}.asm\"", Util.NumberToBaseString(bank, Util.NumberBase.Hexadecimal, 2)); - } - return string.Format("{0," + (length * -1) + "}", s); - } - - private static string GetBankCross(int offset, int length) - { - string s = "check bankcross off"; - return string.Format("{0," + (length * -1) + "}", s); - } - - // length forced to 6 - private static string GetIntermediateAddress(int offset, int length) - { - int ia = Util.GetIntermediateAddressOrPointer(offset); - return ia >= 0 ? Util.NumberToBaseString(ia, Util.NumberBase.Hexadecimal, 6) : " "; - } - - // length forced to 6 - private static string GetProgramCounter(int offset, int length) - { - return Util.NumberToBaseString(Util.ConvertPCtoSNES(offset), Util.NumberBase.Hexadecimal, 6); - } - - // trim to length - private static string GetOffset(int offset, int length) - { - return string.Format("{0," + (length * -1) + "}", Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 0)); - } - - // length forced to 8 - private static string GetRawBytes(int offset, int length) - { - string bytes = ""; - if (Data.GetFlag(offset) == Data.FlagType.Opcode) - { - for (int i = 0; i < Manager.GetInstructionLength(offset); i++) - { - bytes += Util.NumberToBaseString(Data.GetROMByte(offset + i), Util.NumberBase.Hexadecimal); - } - } - return string.Format("{0,-8}", bytes); - } - - // trim to length - private static string GetComment(int offset, int length) - { - return string.Format("{0," + (length * -1) + "}", Data.GetComment(Util.ConvertPCtoSNES(offset))); - } - - // length forced to 2 - private static string GetDataBank(int offset, int length) - { - return Util.NumberToBaseString(Data.GetDataBank(offset), Util.NumberBase.Hexadecimal, 2); - } - - // length forced to 4 - private static string GetDirectPage(int offset, int length) - { - return Util.NumberToBaseString(Data.GetDirectPage(offset), Util.NumberBase.Hexadecimal, 4); - } - - // if length == 1, M/m, else 08/16 - private static string GetMFlag(int offset, int length) - { - bool m = Data.GetMFlag(offset); - if (length == 1) return m ? "M" : "m"; - else return m ? "08" : "16"; - } - - // if length == 1, X/x, else 08/16 - private static string GetXFlag(int offset, int length) - { - bool x = Data.GetXFlag(offset); - if (length == 1) return x ? "X" : "x"; - else return x ? "08" : "16"; - } - - // output label at snes offset, and its value - private static string GetLabelAssign(int offset, int length) - { - var labelName = Data.GetLabelName(offset); - var offsetStr = Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 6, true); - var labelComment = Data.GetLabelComment(offset); - - if (string.IsNullOrEmpty(labelName)) - return ""; - - if (labelComment == null) - labelComment = ""; - - var finalCommentText = ""; - - // TODO: sorry, probably not the best way to stuff this in here, consider putting it in the %comment% section in the future. -Dom - if (printLabelSpecificComments && labelComment != "") - finalCommentText = $"; !^ {labelComment} ^!"; - - string s = $"{labelName} = {offsetStr}{finalCommentText}"; - return string.Format("{0," + (length * -1) + "}", s); - } - } -} diff --git a/DiztinGUIsh/static/Project.cs b/DiztinGUIsh/static/Project.cs deleted file mode 100644 index de80d4e5..00000000 --- a/DiztinGUIsh/static/Project.cs +++ /dev/null @@ -1,416 +0,0 @@ -using DiztinGUIsh.window; -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace DiztinGUIsh -{ - public static class Project - { - public const int HEADER_SIZE = 0x100; - - public static string currentFile = null, currentROMFile = null; - public static bool unsavedChanges = false; - public static string watermark = "DiztinGUIsh"; - - public static bool NewProject(string filename) - { - try - { - byte[] smc = File.ReadAllBytes(filename); - byte[] rom = new byte[smc.Length & 0x7FFFFC00]; - - if ((smc.Length & 0x3FF) == 0x200) for (int i = 0; i < rom.Length; i++) rom[i] = smc[i + 0x200]; - else if ((smc.Length & 0x3FF) != 0) throw new Exception("This ROM has an unusual size. It can't be opened."); - else rom = smc; - - if (rom.Length < 0x8000) throw new Exception("This ROM is too small. It can't be opened."); - - currentROMFile = filename; - - ImportROMDialog import = new ImportROMDialog(rom); - DialogResult result = import.ShowDialog(); - if (result == DialogResult.OK) - { - Data.Initiate(rom, import.GetROMMapMode(), import.GetROMSpeed()); - unsavedChanges = false; - currentFile = null; - - AliasList.me.Reset(); - Dictionary generatedLabels = import.GetGeneratedLabels(); - if (generatedLabels.Count > 0) - { - foreach (KeyValuePair pair in generatedLabels) Data.AddLabel(pair.Key, pair.Value, true); - unsavedChanges = true; - } - - Dictionary generatedFlags = import.GetHeaderFlags(); - if (generatedFlags.Count > 0) - { - foreach (KeyValuePair pair in generatedFlags) Data.SetFlag(pair.Key, pair.Value); - unsavedChanges = true; - } - - return true; - } - } - catch (Exception e) - { - MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - return false; - } - - private const int LATEST_FILE_FORMAT_VERSION = 2; - - public static void SaveProject(string filename) - { - try - { - const int versionToSave = LATEST_FILE_FORMAT_VERSION; - - byte[] data = SaveVersion(versionToSave); - byte[] everything = new byte[HEADER_SIZE + data.Length]; - everything[0] = versionToSave; - Util.StringToByteArray(watermark).CopyTo(everything, 1); - data.CopyTo(everything, HEADER_SIZE); - - if (!IsUncompressedProject(filename)) everything = TryZip(everything); - - File.WriteAllBytes(filename, everything); - unsavedChanges = false; - currentFile = filename; - } - catch (Exception e) - { - MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private static byte[] SaveVersion(int version) - { - void SaveStringToBytes(string str, List bytes) - { - // TODO: combine with Util.StringToByteArray() probably. - if (str != null) - { - foreach (var c in str) - { - bytes.Add((byte)c); - } - } - bytes.Add(0); - } - - if (version < 1 || version > LATEST_FILE_FORMAT_VERSION) - { - throw new ArgumentException($"Saving: Invalid save version requested for saving: {version}."); - } - - int size = Data.GetROMSize(); - byte[] romSettings = new byte[31]; - romSettings[0] = (byte)Data.GetROMMapMode(); - romSettings[1] = (byte)Data.GetROMSpeed(); - Util.IntegerIntoByteArray(size, romSettings, 2); - for (int i = 0; i < 0x15; i++) romSettings[6 + i] = (byte)Data.GetROMByte(Util.ConvertSNEStoPC(0xFFC0 + i)); - for (int i = 0; i < 4; i++) romSettings[27 + i] = (byte)Data.GetROMByte(Util.ConvertSNEStoPC(0xFFDC + i)); - - // TODO put selected offset in save file - - List label = new List(), comment = new List(); - var all_labels = Data.GetAllLabels(); - var all_comments = Data.GetAllComments(); - - Util.IntegerIntoByteList(all_labels.Count, label); - foreach (var pair in all_labels) - { - Util.IntegerIntoByteList(pair.Key, label); - - SaveStringToBytes(pair.Value.name, label); - if (version >= 2) - { - SaveStringToBytes(pair.Value.comment, label); - } - } - - Util.IntegerIntoByteList(all_comments.Count, comment); - foreach (KeyValuePair pair in all_comments) - { - Util.IntegerIntoByteList(pair.Key, comment); - SaveStringToBytes(pair.Value, comment); - } - - byte[] romLocation = Util.StringToByteArray(currentROMFile); - - byte[] data = new byte[romSettings.Length + romLocation.Length + 8 * size + label.Count + comment.Count]; - romSettings.CopyTo(data, 0); - for (int i = 0; i < romLocation.Length; i++) data[romSettings.Length + i] = romLocation[i]; - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + i] = (byte)Data.GetDataBank(i); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + size + i] = (byte)Data.GetDirectPage(i); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + 2 * size + i] = (byte)(Data.GetDirectPage(i) >> 8); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + 3 * size + i] = (byte)(Data.GetXFlag(i) ? 1 : 0); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + 4 * size + i] = (byte)(Data.GetMFlag(i) ? 1 : 0); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + 5 * size + i] = (byte)Data.GetFlag(i); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + 6 * size + i] = (byte)Data.GetArchitechture(i); - for (int i = 0; i < size; i++) data[romSettings.Length + romLocation.Length + 7 * size + i] = (byte)Data.GetInOutPoint(i); - // ??? - label.CopyTo(data, romSettings.Length + romLocation.Length + 8 * size); - comment.CopyTo(data, romSettings.Length + romLocation.Length + 8 * size + label.Count); - // ??? - - return data; - } - - public static bool TryOpenProject(string filename, OpenFileDialog open) - { - try - { - byte[] data = File.ReadAllBytes(filename); - - if (!IsUncompressedProject(filename)) data = TryUnzip(data); - - for (int i = 0; i < watermark.Length; i++) - { - if (data[i + 1] != (byte)watermark[i]) - { - throw new Exception("This is not a valid DiztinGUIsh file!"); - } - } - - byte version = data[0]; - OpenProject(version, data, open); - - unsavedChanges = false; - currentFile = filename; - return true; - } - catch (Exception e) - { - MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - return false; - } - } - - private delegate int AddressConverter(int address); - - private static void OpenProject(int version, byte[] unzipped, OpenFileDialog open) - { - if (version > LATEST_FILE_FORMAT_VERSION) - { - throw new ArgumentException("This DiztinGUIsh file uses a newer file format! You'll need to download the newest version of DiztinGUIsh to open it."); - } - else if (version != LATEST_FILE_FORMAT_VERSION) - { - MessageBox.Show( - "This project file is in an older format.\n" + - "You may want to back up your work or 'Save As' in case the conversion goes wrong.\n" + - "The project file will be untouched until it is saved again.", - "Project File Out of Date", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); - } - - if (version < 0) - { - throw new ArgumentException($"Invalid project file version detected: {version}."); - } - - // version 0 needs to convert PC to SNES for some addresses - AddressConverter converter = address => address; - if (version == 0) - converter = Util.ConvertPCtoSNES; - - Data.ROMMapMode mode = (Data.ROMMapMode)unzipped[HEADER_SIZE]; - Data.ROMSpeed speed = (Data.ROMSpeed)unzipped[HEADER_SIZE + 1]; - int size = Util.ByteArrayToInteger(unzipped, HEADER_SIZE + 2); - string romName = "", romLocation = ""; - byte[] rom; - - int pointer = HEADER_SIZE + 6; - for (int i = 0; i < 0x15; i++) romName += (char) unzipped[pointer++]; - int checksums = Util.ByteArrayToInteger(unzipped, pointer); - pointer += 4; - while (unzipped[pointer] != 0) romLocation += (char) unzipped[pointer++]; - pointer++; - - if (ValidateROM(romLocation, romName, checksums, mode, out rom, open)) - { - Data.Initiate(rom, mode, speed); - - for (int i = 0; i < size; i++) Data.SetDataBank(i, unzipped[pointer + i]); - for (int i = 0; i < size; i++) Data.SetDirectPage(i, unzipped[pointer + size + i] | (unzipped[pointer + 2 * size + i] << 8)); - for (int i = 0; i < size; i++) Data.SetXFlag(i, unzipped[pointer + 3 * size + i] != 0); - for (int i = 0; i < size; i++) Data.SetMFlag(i, unzipped[pointer + 4 * size + i] != 0); - for (int i = 0; i < size; i++) Data.SetFlag(i, (Data.FlagType)unzipped[pointer + 5 * size + i]); - for (int i = 0; i < size; i++) Data.SetArchitechture(i, (Data.Architechture)unzipped[pointer + 6 * size + i]); - for (int i = 0; i < size; i++) Data.SetInOutPoint(i, (Data.InOutPoint)unzipped[pointer + 7 * size + i]); - pointer += 8 * size; - - AliasList.me.Reset(); - ReadAliases(unzipped, ref pointer, converter, version >= 2); - - ReadComments(unzipped, ref pointer, converter); - } - else - { - throw new Exception("Couldn't open the ROM file!"); - } - } - - // TODO: refactor ReadComments and ReadAliases into one generic list-reading function - - private static void ReadComments(byte[] unzipped, ref int pointer, AddressConverter converter) - { - var count = Util.ByteArrayToInteger(unzipped, pointer); - pointer += 4; - - for (var i = 0; i < count; i++) - { - int offset = converter(Util.ByteArrayToInteger(unzipped, pointer)); - pointer += 4; - - var comment = Util.ReadZipString(unzipped, ref pointer); - - Data.AddComment(offset, comment, true); - } - } - - private static void ReadAliases(byte[] unzipped, ref int pointer, AddressConverter converter, bool readAliasComments) - { - int count = Util.ByteArrayToInteger(unzipped, pointer); - pointer += 4; - - for (int i = 0; i < count; i++) - { - int offset = converter(Util.ByteArrayToInteger(unzipped, pointer)); - pointer += 4; - - var aliasInfo = new Data.AliasInfo { - name = Util.ReadZipString(unzipped, ref pointer), - comment = readAliasComments ? Util.ReadZipString(unzipped, ref pointer) : "", - }; - aliasInfo.CleanUp(); - - Data.AddLabel(offset, aliasInfo, true); - } - } - - private static bool ValidateROM(string filename, string romName, int checksums, Data.ROMMapMode mode, out byte[] rom, OpenFileDialog open) - { - bool validFile = false, matchingROM = false; - rom = null; - open.InitialDirectory = currentFile; - - while (!matchingROM) - { - string error = null; - matchingROM = false; - - while (!validFile) - { - error = null; - validFile = false; - - try - { - byte[] smc = File.ReadAllBytes(filename); - rom = new byte[smc.Length & 0x7FFFFC00]; - - if ((smc.Length & 0x3FF) == 0x200) for (int i = 0; i < rom.Length; i++) rom[i] = smc[i + 0x200]; - else if ((smc.Length & 0x3FF) != 0) error = "The linked ROM has an unusual size. It can't be opened."; - else rom = smc; - - if (error == null) validFile = true; - } - catch (Exception) - { - error = string.Format("The linked ROM file '{0}' couldn't be found.", filename); - } - - if (!validFile) - { - DialogResult result = MessageBox.Show(string.Format("{0} Link a new ROM now?", error), "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error); - if (result == DialogResult.No) return false; - result = open.ShowDialog(); - if (result == DialogResult.OK) filename = open.FileName; - else return false; - } - } - - validFile = false; - int offset = Data.GetRomSettingOffset(mode); - if (rom.Length <= offset + 10) error = "The linked ROM is too small. It can't be opened."; - - string myName = ""; - for (int i = 0; i < 0x15; i++) myName += (char)rom[offset - 0x15 + i]; - int myChecksums = Util.ByteArrayToInteger(rom, offset + 7); - - if (myName != romName) error = string.Format("The linked ROM's internal name '{0}' doesn't match the project's internal name of '{1}'.", myName, romName); - else if (checksums != myChecksums) error = string.Format("The linked ROM's checksums '{0:X8}' don't match the project's checksums of '{1:X8}'.", myChecksums, checksums); - - if (error == null) matchingROM = true; - - if (!matchingROM) - { - DialogResult result = MessageBox.Show(string.Format("{0} Link a new ROM now?", error), "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error); - if (result == DialogResult.No) return false; - result = open.ShowDialog(); - if (result == DialogResult.OK) filename = open.FileName; - else return false; - } - } - - if (currentROMFile != filename) - { - currentROMFile = filename; - unsavedChanges = true; - } - return true; - } - - private static bool IsUncompressedProject(string filename) - { - return Path.GetExtension(filename).Equals(".dizraw", StringComparison.InvariantCultureIgnoreCase); - } - - // https://stackoverflow.com/questions/33119119/unzip-byte-array-in-c-sharp - private static byte[] TryUnzip(byte[] data) - { - try - { - using (MemoryStream comp = new MemoryStream(data)) - using (GZipStream gzip = new GZipStream(comp, CompressionMode.Decompress)) - using (MemoryStream res = new MemoryStream()) - { - gzip.CopyTo(res); - return res.ToArray(); - } - } - catch (Exception) - { - return null; - } - } - - private static byte[] TryZip(byte[] data) - { - try - { - using (MemoryStream comp = new MemoryStream()) - using (GZipStream gzip = new GZipStream(comp, CompressionMode.Compress)) - { - gzip.Write(data, 0, data.Length); - gzip.Close(); - return comp.ToArray(); - } - } - catch (Exception) - { - return null; - } - } - } -} diff --git a/DiztinGUIsh/static/Util.cs b/DiztinGUIsh/static/Util.cs deleted file mode 100644 index 78d97af9..00000000 --- a/DiztinGUIsh/static/Util.cs +++ /dev/null @@ -1,581 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace DiztinGUIsh -{ - public static class Util - { - public enum NumberBase - { - Decimal = 3, Hexadecimal = 2, Binary = 8 - } - - public static int GetROMWord(int offset) - { - if (offset + 1 < Data.GetROMSize()) - return Data.GetROMByte(offset) + (Data.GetROMByte(offset + 1) << 8); - return -1; - } - - public static int GetROMLong(int offset) - { - if (offset + 2 < Data.GetROMSize()) - return Data.GetROMByte(offset) + (Data.GetROMByte(offset + 1) << 8) + (Data.GetROMByte(offset + 2) << 16); - return -1; - } - - public static int GetROMDoubleWord(int offset) - { - if (offset + 3 < Data.GetROMSize()) - return Data.GetROMByte(offset) + (Data.GetROMByte(offset + 1) << 8) + (Data.GetROMByte(offset + 2) << 16) + (Data.GetROMByte(offset + 3) << 24); - return -1; - } - - public static int GetIntermediateAddressOrPointer(int offset) - { - switch (Data.GetFlag(offset)) - { - case Data.FlagType.Unreached: - case Data.FlagType.Opcode: - return GetIntermediateAddress(offset); - case Data.FlagType.Pointer16Bit: - int bank = Data.GetDataBank(offset); - return (bank << 16) | GetROMWord(offset); - case Data.FlagType.Pointer24Bit: - case Data.FlagType.Pointer32Bit: - return GetROMLong(offset); - } - return -1; - } - - public static int GetIntermediateAddress(int offset) - { - switch (Data.GetArchitechture(offset)) - { - case Data.Architechture.CPU65C816: return CPU65C816.GetIntermediateAddress(offset); - case Data.Architechture.APUSPC700: return -1; - case Data.Architechture.GPUSuperFX: return -1; - } - return -1; - } - - public static string GetInstruction(int offset) - { - switch (Data.GetArchitechture(offset)) - { - case Data.Architechture.CPU65C816: return CPU65C816.GetInstruction(offset); - case Data.Architechture.APUSPC700: return ""; - case Data.Architechture.GPUSuperFX: return ""; - } - return ""; - } - - public static string GetFormattedBytes(int offset, int step, int bytes) - { - string res = ""; - switch (step) - { - case 1: res = "db "; break; - case 2: res = "dw "; break; - case 3: res = "dl "; break; - case 4: res = "dd "; break; - } - - for (int i = 0; i < bytes; i += step) - { - if (i > 0) res += ","; - - switch (step) - { - case 1: res += NumberToBaseString(Data.GetROMByte(offset + i), NumberBase.Hexadecimal, 2, true); break; - case 2: res += NumberToBaseString(GetROMWord(offset + i), NumberBase.Hexadecimal, 4, true); break; - case 3: res += NumberToBaseString(GetROMLong(offset + i), NumberBase.Hexadecimal, 6, true); break; - case 4: res += NumberToBaseString(GetROMDoubleWord(offset + i), NumberBase.Hexadecimal, 8, true); break; - } - } - - return res; - } - - public static string GetPointer(int offset, int bytes) - { - int ia = -1; - string format = "", param = ""; - switch (bytes) - { - case 2: - ia = (Data.GetDataBank(offset) << 16) | GetROMWord(offset); - format = "dw {0}"; - param = NumberToBaseString(GetROMWord(offset), NumberBase.Hexadecimal, 4, true); - break; - case 3: - ia = GetROMLong(offset); - format = "dl {0}"; - param = NumberToBaseString(GetROMLong(offset), NumberBase.Hexadecimal, 6, true); - break; - case 4: - ia = GetROMLong(offset); - format = "dl {0}" + string.Format(" : db {0}", NumberToBaseString(Data.GetROMByte(offset + 3), NumberBase.Hexadecimal, 2, true)); - param = NumberToBaseString(GetROMLong(offset), NumberBase.Hexadecimal, 6, true); - break; - } - - int pc = ConvertSNEStoPC(ia); - if (pc >= 0 && Data.GetLabelName(ia) != "") param = Data.GetLabelName(ia); - return string.Format(format, param); - } - - public static string GetFormattedText(int offset, int bytes) - { - string text = "db \""; - for (int i = 0; i < bytes; i++) text += (char)Data.GetROMByte(offset + i); - return text + "\""; - } - - public static string GetDefaultLabel(int address) - { - int pc = ConvertSNEStoPC(address); - return string.Format("{0}_{1}", TypeToLabel(Data.GetFlag(pc)), NumberToBaseString(address, NumberBase.Hexadecimal, 6)); - } - - public static int ConvertPCtoSNES(int offset) - { - if (Data.GetROMMapMode() == Data.ROMMapMode.LoROM) - { - offset = ((offset & 0x3F8000) << 1) | 0x8000 | (offset & 0x7FFF); - if (Data.GetROMSpeed() == Data.ROMSpeed.FastROM || offset >= 0x7E0000) offset |= 0x800000; - } - else if (Data.GetROMMapMode() == Data.ROMMapMode.HiROM) - { - offset |= 0x400000; - if (Data.GetROMSpeed() == Data.ROMSpeed.FastROM || offset >= 0x7E0000) offset |= 0x800000; - } - else if (Data.GetROMMapMode() == Data.ROMMapMode.ExHiROM) - { - if (offset < 0x40000) offset |= 0xC00000; - else if (offset >= 0x7E0000) offset &= 0x3FFFFF; - } - else - { - if (offset >= 0x400000 && Data.GetROMMapMode() == Data.ROMMapMode.ExSA1ROM) - { - offset += 0x800000; - } - else - { - offset = ((offset & 0x3F8000) << 1) | 0x8000 | (offset & 0x7FFF); - if (offset >= 0x400000) offset += 0x400000; - } - } - return offset; - } - - public static int ConvertSNEStoPC(int address) - { - // WRAM is N/A to PC addressing - if ((address & 0xFE0000) == 0x7E0000) return -1; - - // WRAM mirror & PPU regs are N/A to PC addressing - if (((address & 0x400000) == 0) && ((address & 0x8000) == 0)) return -1; - - switch (Data.GetROMMapMode()) - { - case Data.ROMMapMode.LoROM: - { - // SRAM is N/A to PC addressing - if (((address & 0x700000) == 0x700000) && ((address & 0x8000) == 0)) return -1; - - return UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); - } - case Data.ROMMapMode.HiROM: - { - return UnmirroredOffset(address & 0x3FFFFF); - } - case Data.ROMMapMode.SuperMMC: - { - return UnmirroredOffset(address & 0x3FFFFF); // todo, treated as hirom atm - } - case Data.ROMMapMode.SA1ROM: - case Data.ROMMapMode.ExSA1ROM: - { - // BW-RAM is N/A to PC addressing - if (address >= 0x400000 && address <= 0x7FFFFF) return -1; - - if (address >= 0xC00000) - { - if (Data.GetROMMapMode() == Data.ROMMapMode.ExSA1ROM) - return UnmirroredOffset(address & 0x7FFFFF); - else - return UnmirroredOffset(address & 0x3FFFFF); - } - else - { - if (address >= 0x800000) address -= 0x400000; - - // SRAM is N/A to PC addressing - if (((address & 0x8000) == 0)) return -1; - - return UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); - } - } - case Data.ROMMapMode.SuperFX: - { - // BW-RAM is N/A to PC addressing - if (address >= 0x600000 && address <= 0x7FFFFF) return -1; - - if (address < 0x400000) - { - return UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); - } else if (address < 0x600000) - { - return UnmirroredOffset(address & 0x3FFFFF); - } else if (address < 0xC00000) - { - return 0x200000 + UnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF)); - } else - { - return 0x400000 + UnmirroredOffset(address & 0x3FFFFF); - } - } - case Data.ROMMapMode.ExHiROM: - { - return UnmirroredOffset(((~address & 0x800000) >> 1) | (address & 0x3FFFFF)); - } - case Data.ROMMapMode.ExLoROM: - { - // SRAM is N/A to PC addressing - if (((address & 0x700000) == 0x700000) && ((address & 0x8000) == 0)) return -1; - - return UnmirroredOffset((((address ^ 0x800000) & 0xFF0000) >> 1) | (address & 0x7FFF)); - } - default: - { - return -1; - } - } - } - - public static string ReadZipString(byte[] unzipped, ref int pointer) - { - var label = ""; - while (unzipped[pointer] != 0) - label += (char)unzipped[pointer++]; - pointer++; - return label; - } - - private static int UnmirroredOffset(int offset) - { - int size = Data.GetROMSize(); - - // most of the time this is true; for efficiency - if (offset < size) return offset; - - int repeatSize = 0x8000; - while (repeatSize < size) repeatSize <<= 1; - - int repeatedOffset = offset % repeatSize; - - // this will then be true for ROM sizes of powers of 2 - if (repeatedOffset < size) return repeatedOffset; - - // for ROM sizes not powers of 2, it's kinda ugly - int sizeOfSmallerSection = 0x8000; - while (size % (sizeOfSmallerSection << 1) == 0) sizeOfSmallerSection <<= 1; - - while (repeatedOffset >= size) repeatedOffset -= sizeOfSmallerSection; - return repeatedOffset; - } - - public static byte[] IntegerToByteArray(int a) - { - return new byte[] - { - (byte)a, - (byte)(a >> 8), - (byte)(a >> 16), - (byte)(a >> 24) - }; - } - - public static void IntegerIntoByteArray(int a, byte[] data, int offset) - { - byte[] arr = IntegerToByteArray(a); - for (int i = 0; i < arr.Length; i++) data[offset + i] = arr[i]; - } - - public static void IntegerIntoByteList(int a, List list) - { - byte[] arr = IntegerToByteArray(a); - for (int i = 0; i < arr.Length; i++) list.Add(arr[i]); - } - - public static int ByteArrayToInteger(byte[] data, int offset = 0) - { - return - data[offset] | - (data[offset + 1] << 8) | - (data[offset + 2] << 16) | - (data[offset + 3] << 24); - } - - // deal with addresses that look like this, - // might be pasted from other editors - // C0FFFF - // $C0FFFF - // C7/AAAA - // $C6/BBBB - public static bool StripFormattedAddress(ref string addressTxt, NumberStyles style, out int address) - { - address = -1; - - if (string.IsNullOrEmpty(addressTxt)) - return false; - - var inputText = new string(Array.FindAll(addressTxt.ToCharArray(), (c => - (char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)) - ))); - - if (int.TryParse(inputText, style, null, out address)) - { - addressTxt = inputText; - return true; - } - - return false; - } - - public static string TypeToString(Data.FlagType flag) - { - switch (flag) - { - case Data.FlagType.Unreached: return "Unreached"; - case Data.FlagType.Opcode: return "Opcode"; - case Data.FlagType.Operand: return "Operand"; - case Data.FlagType.Data8Bit: return "Data (8-bit)"; - case Data.FlagType.Graphics: return "Graphics"; - case Data.FlagType.Music: return "Music"; - case Data.FlagType.Empty: return "Empty"; - case Data.FlagType.Data16Bit: return "Data (16-bit)"; - case Data.FlagType.Pointer16Bit: return "Pointer (16-bit)"; - case Data.FlagType.Data24Bit: return "Data (24-bit)"; - case Data.FlagType.Pointer24Bit: return "Pointer (24-bit)"; - case Data.FlagType.Data32Bit: return "Data (32-bit)"; - case Data.FlagType.Pointer32Bit: return "Pointer (32-bit)"; - case Data.FlagType.Text: return "Text"; - } - return ""; - } - - public static string TypeToLabel(Data.FlagType flag) - { - switch (flag) - { - case Data.FlagType.Unreached: return "UNREACH"; - case Data.FlagType.Opcode: return "CODE"; - case Data.FlagType.Operand: return "LOOSE_OP"; - case Data.FlagType.Data8Bit: return "DATA8"; - case Data.FlagType.Graphics: return "GFX"; - case Data.FlagType.Music: return "MUSIC"; - case Data.FlagType.Empty: return "EMPTY"; - case Data.FlagType.Data16Bit: return "DATA16"; - case Data.FlagType.Pointer16Bit: return "PTR16"; - case Data.FlagType.Data24Bit: return "DATA24"; - case Data.FlagType.Pointer24Bit: return "PTR24"; - case Data.FlagType.Data32Bit: return "DATA32"; - case Data.FlagType.Pointer32Bit: return "PTR32"; - case Data.FlagType.Text: return "TEXT"; - } - return ""; - } - - public static int TypeStepSize(Data.FlagType flag) - { - switch (flag) - { - case Data.FlagType.Unreached: - case Data.FlagType.Opcode: - case Data.FlagType.Operand: - case Data.FlagType.Data8Bit: - case Data.FlagType.Graphics: - case Data.FlagType.Music: - case Data.FlagType.Empty: - case Data.FlagType.Text: - return 1; - case Data.FlagType.Data16Bit: - case Data.FlagType.Pointer16Bit: - return 2; - case Data.FlagType.Data24Bit: - case Data.FlagType.Pointer24Bit: - return 3; - case Data.FlagType.Data32Bit: - case Data.FlagType.Pointer32Bit: - return 4; - } - return 0; - } - - public static string ArchToString(Data.Architechture arch) - { - switch (arch) - { - case Data.Architechture.CPU65C816: return "65C816"; - case Data.Architechture.APUSPC700: return "SPC700"; - case Data.Architechture.GPUSuperFX: return "SuperFX"; - } - return ""; - } - - public static string PointToString(Data.InOutPoint point) - { - string result; - - if ((point & Data.InOutPoint.EndPoint) == Data.InOutPoint.EndPoint) result = "X"; - else if ((point & Data.InOutPoint.OutPoint) == Data.InOutPoint.OutPoint) result = "<"; - else result = " "; - - result += ((point & Data.InOutPoint.ReadPoint) == Data.InOutPoint.ReadPoint) ? "*" : " "; - result += ((point & Data.InOutPoint.InPoint) == Data.InOutPoint.InPoint) ? ">" : " "; - - return result; - } - - public static string BoolToSize(bool b) - { - return b ? "8" : "16"; - } - - public static string NumberToBaseString(int v, NumberBase noBase, int d = -1, bool showPrefix = false) - { - int digits = d < 0 ? (int)noBase : d; - switch (noBase) - { - case NumberBase.Decimal: - if (digits == 0) return v.ToString("D"); - return v.ToString("D" + digits); - case NumberBase.Hexadecimal: - if (digits == 0) return v.ToString("X"); - return (showPrefix ? "$" : "") + v.ToString("X" + digits); - case NumberBase.Binary: - string b = ""; - int i = 0; - while (digits == 0 ? v > 0 : i < digits) - { - b += (v & 1); - v >>= 1; - i++; - } - return (showPrefix ? "%" : "") + b; - } - return ""; - } - - public static byte[] StringToByteArray(string s) - { - byte[] array = new byte[s.Length + 1]; - for (int i = 0; i < s.Length; i++) array[i] = (byte)s[i]; - array[s.Length] = 0; - return array; - } - - public static void PaintCell(int offset, DataGridViewCellStyle style, int column, int selOffset) - { - // editable cells show up green - if (column == 0 || column == 8 || column == 9 || column == 12) style.SelectionBackColor = Color.Chartreuse; - - switch (Data.GetFlag(offset)) - { - case Data.FlagType.Unreached: - style.BackColor = Color.LightGray; - style.ForeColor = Color.DarkSlateGray; - break; - case Data.FlagType.Opcode: - int opcode = Data.GetROMByte(offset); - switch (column) - { - case 4: // <*> - Data.InOutPoint point = Data.GetInOutPoint(offset); - int r = 255, g = 255, b = 255; - if ((point & (Data.InOutPoint.EndPoint | Data.InOutPoint.OutPoint)) != 0) g -= 50; - if ((point & (Data.InOutPoint.InPoint)) != 0) r -= 50; - if ((point & (Data.InOutPoint.ReadPoint)) != 0) b -= 50; - style.BackColor = Color.FromArgb(r, g, b); - break; - case 5: // Instruction - if (opcode == 0x40 || opcode == 0xCB || opcode == 0xDB || opcode == 0xF8 // RTI WAI STP SED - || opcode == 0xFB || opcode == 0x00 || opcode == 0x02 || opcode == 0x42 // XCE BRK COP WDM - ) style.BackColor = Color.Yellow; - break; - case 8: // Data Bank - if (opcode == 0xAB || opcode == 0x44 || opcode == 0x54) // PLB MVP MVN - style.BackColor = Color.OrangeRed; - else if (opcode == 0x8B) // PHB - style.BackColor = Color.Yellow; - break; - case 9: // Direct Page - if (opcode == 0x2B || opcode == 0x5B) // PLD TCD - style.BackColor = Color.OrangeRed; - if (opcode == 0x0B || opcode == 0x7B) // PHD TDC - style.BackColor = Color.Yellow; - break; - case 10: // M Flag - case 11: // X Flag - int mask = column == 10 ? 0x20 : 0x10; - if (opcode == 0x28 || ((opcode == 0xC2 || opcode == 0xE2) // PLP SEP REP - && (Data.GetROMByte(offset + 1) & mask) != 0)) // relevant bit set - style.BackColor = Color.OrangeRed; - if (opcode == 0x08) // PHP - style.BackColor = Color.Yellow; - break; - } - break; - case Data.FlagType.Operand: - style.ForeColor = Color.LightGray; - break; - case Data.FlagType.Graphics: - style.BackColor = Color.LightPink; - break; - case Data.FlagType.Music: - style.BackColor = Color.PowderBlue; - break; - case Data.FlagType.Data8Bit: - case Data.FlagType.Data16Bit: - case Data.FlagType.Data24Bit: - case Data.FlagType.Data32Bit: - style.BackColor = Color.NavajoWhite; - break; - case Data.FlagType.Pointer16Bit: - case Data.FlagType.Pointer24Bit: - case Data.FlagType.Pointer32Bit: - style.BackColor = Color.Orchid; - break; - case Data.FlagType.Text: - style.BackColor = Color.Aquamarine; - break; - case Data.FlagType.Empty: - style.BackColor = Color.DarkSlateGray; - style.ForeColor = Color.LightGray; - break; - } - - if (selOffset >= 0 && selOffset < Data.GetROMSize()) - { - if (column == 1 - //&& (Data.GetFlag(selOffset) == Data.FlagType.Opcode || Data.GetFlag(selOffset) == Data.FlagType.Unreached) - && ConvertSNEStoPC(GetIntermediateAddressOrPointer(selOffset)) == offset - ) style.BackColor = Color.DeepPink; - - if (column == 6 - //&& (Data.GetFlag(offset) == Data.FlagType.Opcode || Data.GetFlag(offset) == Data.FlagType.Unreached) - && ConvertSNEStoPC(GetIntermediateAddressOrPointer(offset)) == selOffset - ) style.BackColor = Color.DeepPink; - } - } - } -} diff --git a/DiztinGUIsh/static/diz/CPU65C816.cs b/DiztinGUIsh/static/diz/CPU65C816.cs deleted file mode 100644 index 6618c6e3..00000000 --- a/DiztinGUIsh/static/diz/CPU65C816.cs +++ /dev/null @@ -1,451 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DiztinGUIsh -{ - public static class CPU65C816 - { - public static int Step(int offset, bool branch, bool force, int prevOffset) - { - int opcode = Data.GetROMByte(offset); - int prevDirectPage = Data.GetDirectPage(offset); - int prevDataBank = Data.GetDataBank(offset); - bool prevX = Data.GetXFlag(offset), prevM = Data.GetMFlag(offset); - - while (prevOffset >= 0 && Data.GetFlag(prevOffset) == Data.FlagType.Operand) prevOffset--; - if (prevOffset >= 0 && Data.GetFlag(prevOffset) == Data.FlagType.Opcode) - { - prevDirectPage = Data.GetDirectPage(prevOffset); - prevDataBank = Data.GetDataBank(prevOffset); - prevX = Data.GetXFlag(prevOffset); - prevM = Data.GetMFlag(prevOffset); - } - - if (opcode == 0xC2 || opcode == 0xE2) // REP SEP - { - prevX = (Data.GetROMByte(offset + 1) & 0x10) != 0 ? opcode == 0xE2 : prevX; - prevM = (Data.GetROMByte(offset + 1) & 0x20) != 0 ? opcode == 0xE2 : prevM; - } - - // set first byte first, so the instruction length is correct - Data.SetFlag(offset, Data.FlagType.Opcode); - Data.SetDataBank(offset, prevDataBank); - Data.SetDirectPage(offset, prevDirectPage); - Data.SetXFlag(offset, prevX); - Data.SetMFlag(offset, prevM); - - int length = GetInstructionLength(offset); - - for (int i = 1; i < length; i++) - { - Data.SetFlag(offset + i, Data.FlagType.Operand); - Data.SetDataBank(offset + i, prevDataBank); - Data.SetDirectPage(offset + i, prevDirectPage); - Data.SetXFlag(offset + i, prevX); - Data.SetMFlag(offset + i, prevM); - } - - MarkInOutPoints(offset); - - int nextOffset = offset + length; - - if (!force && (opcode == 0x4C || opcode == 0x5C || opcode == 0x80 || opcode == 0x82 // JMP JML BRA BRL - || (branch && (opcode == 0x10 || opcode == 0x30 || opcode == 0x50 // BPL BMI BVC - || opcode == 0x70 || opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 // BVS BCC BCS BNE - || opcode == 0xF0 || opcode == 0x20 || opcode == 0x22)))) // BEQ JSR JSL - { - int iaNextOffsetPC = Util.ConvertSNEStoPC(Util.GetIntermediateAddress(offset)); - if (iaNextOffsetPC >= 0) nextOffset = iaNextOffsetPC; - } - - return nextOffset; - } - - public static int GetIntermediateAddress(int offset) - { - int bank, directPage, operand, programCounter; - int opcode = Data.GetROMByte(offset); - - AddressMode mode = GetAddressMode(offset); - switch (mode) - { - case AddressMode.DIRECT_PAGE: - case AddressMode.DIRECT_PAGE_X_INDEX: - case AddressMode.DIRECT_PAGE_Y_INDEX: - case AddressMode.DIRECT_PAGE_INDIRECT: - case AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT: - case AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX: - case AddressMode.DIRECT_PAGE_LONG_INDIRECT: - case AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX: - directPage = Data.GetDirectPage(offset); - operand = Data.GetROMByte(offset + 1); - return (directPage + operand) & 0xFFFF; - case AddressMode.DIRECT_PAGE_S_INDEX: - case AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX: - return Data.GetROMByte(offset + 1); - case AddressMode.ADDRESS: - case AddressMode.ADDRESS_X_INDEX: - case AddressMode.ADDRESS_Y_INDEX: - case AddressMode.ADDRESS_X_INDEX_INDIRECT: - bank = (opcode == 0x20 || opcode == 0x4C || opcode == 0x7C || opcode == 0xFC) ? - Util.ConvertPCtoSNES(offset) >> 16 : - Data.GetDataBank(offset); - operand = Util.GetROMWord(offset + 1); - return (bank << 16) | operand; - case AddressMode.ADDRESS_INDIRECT: - case AddressMode.ADDRESS_LONG_INDIRECT: - operand = Util.GetROMWord(offset + 1); - return operand; - case AddressMode.LONG: - case AddressMode.LONG_X_INDEX: - operand = Util.GetROMLong(offset + 1); - return operand; - case AddressMode.RELATIVE_8: - programCounter = Util.ConvertPCtoSNES(offset + 2); - bank = programCounter >> 16; - offset = (sbyte)Data.GetROMByte(offset + 1); - return (bank << 16) | ((programCounter + offset) & 0xFFFF); - case AddressMode.RELATIVE_16: - programCounter = Util.ConvertPCtoSNES(offset + 3); - bank = programCounter >> 16; - offset = (short)Util.GetROMWord(offset + 1); - return (bank << 16) | ((programCounter + offset) & 0xFFFF); - } - return -1; - } - - public static string GetInstruction(int offset) - { - AddressMode mode = GetAddressMode(offset); - string format = GetInstructionFormatString(offset); - string mnemonic = GetMnemonic(offset); - string op1 = "", op2 = ""; - if (mode == AddressMode.BLOCK_MOVE) - { - op1 = Util.NumberToBaseString(Data.GetROMByte(offset + 1), Util.NumberBase.Hexadecimal, 2, true); - op2 = Util.NumberToBaseString(Data.GetROMByte(offset + 2), Util.NumberBase.Hexadecimal, 2, true); - } - else if (mode == AddressMode.CONSTANT_8 || mode == AddressMode.IMMEDIATE_8) - { - op1 = Util.NumberToBaseString(Data.GetROMByte(offset + 1), Util.NumberBase.Hexadecimal, 2, true); - } - else if (mode == AddressMode.IMMEDIATE_16) - { - op1 = Util.NumberToBaseString(Util.GetROMWord(offset + 1), Util.NumberBase.Hexadecimal, 4, true); - } - else - { - op1 = FormatOperandAddress(offset, mode); - } - return string.Format(format, mnemonic, op1, op2); - } - - public static int GetInstructionLength(int offset) - { - AddressMode mode = GetAddressMode(offset); - switch (mode) - { - case AddressMode.IMPLIED: - case AddressMode.ACCUMULATOR: - return 1; - case AddressMode.CONSTANT_8: - case AddressMode.IMMEDIATE_8: - case AddressMode.DIRECT_PAGE: - case AddressMode.DIRECT_PAGE_X_INDEX: - case AddressMode.DIRECT_PAGE_Y_INDEX: - case AddressMode.DIRECT_PAGE_S_INDEX: - case AddressMode.DIRECT_PAGE_INDIRECT: - case AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT: - case AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX: - case AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX: - case AddressMode.DIRECT_PAGE_LONG_INDIRECT: - case AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX: - case AddressMode.RELATIVE_8: - return 2; - case AddressMode.IMMEDIATE_16: - case AddressMode.ADDRESS: - case AddressMode.ADDRESS_X_INDEX: - case AddressMode.ADDRESS_Y_INDEX: - case AddressMode.ADDRESS_INDIRECT: - case AddressMode.ADDRESS_X_INDEX_INDIRECT: - case AddressMode.ADDRESS_LONG_INDIRECT: - case AddressMode.BLOCK_MOVE: - case AddressMode.RELATIVE_16: - return 3; - case AddressMode.LONG: - case AddressMode.LONG_X_INDEX: - return 4; - } - return 1; - } - - public static void MarkInOutPoints(int offset) - { - int opcode = Data.GetROMByte(offset); - int iaOffsetPC = Util.ConvertSNEStoPC(Util.GetIntermediateAddress(offset)); - - // set read point on EA - if (iaOffsetPC >= 0 && ( // these are all read/write/math instructions - ((opcode & 0x04) != 0) || ((opcode & 0x0F) == 0x01) || ((opcode & 0x0F) == 0x03) || - ((opcode & 0x1F) == 0x12) || ((opcode & 0x1F) == 0x19)) && - (opcode != 0x45) && (opcode != 0x55) && (opcode != 0xF5) && (opcode != 0x4C) && - (opcode != 0x5C) && (opcode != 0x6C) && (opcode != 0x7C) && (opcode != 0xDC) && (opcode != 0xFC) - ) Data.SetInOutPoint(iaOffsetPC, Data.InOutPoint.ReadPoint); - - // set end point on offset - if (opcode == 0x40 || opcode == 0x4C || opcode == 0x5C || opcode == 0x60 // RTI JMP JML RTS - || opcode == 0x6B || opcode == 0x6C || opcode == 0x7C || opcode == 0x80 // RTL JMP JMP BRA - || opcode == 0x82 || opcode == 0xDB || opcode == 0xDC // BRL STP JML - ) Data.SetInOutPoint(offset, Data.InOutPoint.EndPoint); - - // set out point on offset - // set in point on EA - if (iaOffsetPC >= 0 && ( - opcode == 0x4C || opcode == 0x5C || opcode == 0x80 || opcode == 0x82 // JMP JML BRA BRL - || opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 // BPL BMI BVC BVS - || opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0 // BCC BCS BNE BEQ - || opcode == 0x20 || opcode == 0x22)) // JSR JSL - { - Data.SetInOutPoint(offset, Data.InOutPoint.OutPoint); - Data.SetInOutPoint(iaOffsetPC, Data.InOutPoint.InPoint); - } - } - - private static string FormatOperandAddress(int offset, AddressMode mode) - { - int address = Util.GetIntermediateAddress(offset); - if (address < 0) return ""; - if (Data.GetLabelName(address) != "") return Data.GetLabelName(address); - - int count = BytesToShow(mode); - if (mode == AddressMode.RELATIVE_8 || mode == AddressMode.RELATIVE_16) address = Util.GetROMWord(offset + 1); - address &= ~(-1 << (8 * count)); - return Util.NumberToBaseString(address, Util.NumberBase.Hexadecimal, 2 * count, true); - } - - private static string GetMnemonic(int offset, bool showHint = true) - { - string mn = mnemonics[Data.GetROMByte(offset)]; - if (showHint) - { - AddressMode mode = GetAddressMode(offset); - int count = BytesToShow(mode); - - if (mode == AddressMode.CONSTANT_8 || mode == AddressMode.RELATIVE_16 || mode == AddressMode.RELATIVE_8) return mn; - - switch (count) - { - case 1: return mn += ".B"; - case 2: return mn += ".W"; - case 3: return mn += ".L"; - } - } - return mn; - } - - private static int BytesToShow(AddressMode mode) - { - switch (mode) - { - case AddressMode.CONSTANT_8: - case AddressMode.IMMEDIATE_8: - case AddressMode.DIRECT_PAGE: - case AddressMode.DIRECT_PAGE_X_INDEX: - case AddressMode.DIRECT_PAGE_Y_INDEX: - case AddressMode.DIRECT_PAGE_S_INDEX: - case AddressMode.DIRECT_PAGE_INDIRECT: - case AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT: - case AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX: - case AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX: - case AddressMode.DIRECT_PAGE_LONG_INDIRECT: - case AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX: - case AddressMode.RELATIVE_8: - return 1; - case AddressMode.IMMEDIATE_16: - case AddressMode.ADDRESS: - case AddressMode.ADDRESS_X_INDEX: - case AddressMode.ADDRESS_Y_INDEX: - case AddressMode.ADDRESS_INDIRECT: - case AddressMode.ADDRESS_X_INDEX_INDIRECT: - case AddressMode.ADDRESS_LONG_INDIRECT: - case AddressMode.RELATIVE_16: - return 2; - case AddressMode.LONG: - case AddressMode.LONG_X_INDEX: - return 3; - } - return 0; - } - - // {0} = mnemonic - // {1} = intermediate address / label OR operand 1 for block move - // {2} = operand 2 for block move - private static string GetInstructionFormatString(int offset) - { - AddressMode mode = GetAddressMode(offset); - switch (mode) - { - case AddressMode.IMPLIED: - return "{0}"; - case AddressMode.ACCUMULATOR: - return "{0} A"; - case AddressMode.CONSTANT_8: - case AddressMode.IMMEDIATE_8: - case AddressMode.IMMEDIATE_16: - return "{0} #{1}"; - case AddressMode.DIRECT_PAGE: - case AddressMode.ADDRESS: - case AddressMode.LONG: - case AddressMode.RELATIVE_8: - case AddressMode.RELATIVE_16: - return "{0} {1}"; - case AddressMode.DIRECT_PAGE_X_INDEX: - case AddressMode.ADDRESS_X_INDEX: - case AddressMode.LONG_X_INDEX: - return "{0} {1},X"; - case AddressMode.DIRECT_PAGE_Y_INDEX: - case AddressMode.ADDRESS_Y_INDEX: - return "{0} {1},Y"; - case AddressMode.DIRECT_PAGE_S_INDEX: - return "{0} {1},S"; - case AddressMode.DIRECT_PAGE_INDIRECT: - case AddressMode.ADDRESS_INDIRECT: - return "{0} ({1})"; - case AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT: - case AddressMode.ADDRESS_X_INDEX_INDIRECT: - return "{0} ({1},X)"; - case AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX: - return "{0} ({1}),Y"; - case AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX: - return "{0} ({1},S),Y"; - case AddressMode.DIRECT_PAGE_LONG_INDIRECT: - case AddressMode.ADDRESS_LONG_INDIRECT: - return "{0} [{1}]"; - case AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX: - return "{0} [{1}],Y"; - case AddressMode.BLOCK_MOVE: - return "{0} {1},{2}"; - } - return ""; - } - - private static AddressMode GetAddressMode(int offset) - { - AddressMode mode = addressingModes[Data.GetROMByte(offset)]; - if (mode == AddressMode.IMMEDIATE_M_FLAG_DEPENDENT) return Data.GetMFlag(offset) ? AddressMode.IMMEDIATE_8 : AddressMode.IMMEDIATE_16; - else if (mode == AddressMode.IMMEDIATE_X_FLAG_DEPENDENT) return Data.GetXFlag(offset) ? AddressMode.IMMEDIATE_8 : AddressMode.IMMEDIATE_16; - return mode; - } - - private enum AddressMode : byte - { - IMPLIED, ACCUMULATOR, CONSTANT_8, IMMEDIATE_8, IMMEDIATE_16, - IMMEDIATE_X_FLAG_DEPENDENT, IMMEDIATE_M_FLAG_DEPENDENT, - DIRECT_PAGE, DIRECT_PAGE_X_INDEX, DIRECT_PAGE_Y_INDEX, - DIRECT_PAGE_S_INDEX, DIRECT_PAGE_INDIRECT, DIRECT_PAGE_X_INDEX_INDIRECT, - DIRECT_PAGE_INDIRECT_Y_INDEX, DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - DIRECT_PAGE_LONG_INDIRECT, DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - ADDRESS, ADDRESS_X_INDEX, ADDRESS_Y_INDEX, ADDRESS_INDIRECT, - ADDRESS_X_INDEX_INDIRECT, ADDRESS_LONG_INDIRECT, - LONG, LONG_X_INDEX, BLOCK_MOVE, RELATIVE_8, RELATIVE_16 - } - - private static string[] mnemonics = - { - "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", - "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", - "JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", - "BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", - "RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", - "BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", "CLI", "EOR", "PHY", "TCD", "JML", "EOR", "LSR", "EOR", - "RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", - "BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", - "BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", - "BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", - "LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", - "BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", - "CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", - "BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", - "CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", - "BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" - }; - - private static AddressMode[] addressingModes = - { - AddressMode.CONSTANT_8, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.CONSTANT_8, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.ACCUMULATOR, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.ACCUMULATOR, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.ADDRESS, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.LONG, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.ACCUMULATOR, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.ACCUMULATOR, AddressMode.IMPLIED, - AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.IMPLIED, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.CONSTANT_8, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.BLOCK_MOVE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.ACCUMULATOR, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.BLOCK_MOVE, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.LONG, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.IMPLIED, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.RELATIVE_16, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.ACCUMULATOR, AddressMode.IMPLIED, - AddressMode.ADDRESS_INDIRECT, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS_X_INDEX_INDIRECT, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.RELATIVE_16, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_Y_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.IMMEDIATE_X_FLAG_DEPENDENT, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.IMMEDIATE_X_FLAG_DEPENDENT, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_Y_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_Y_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.IMMEDIATE_X_FLAG_DEPENDENT, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.CONSTANT_8, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS_LONG_INDIRECT, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - - AddressMode.IMMEDIATE_X_FLAG_DEPENDENT, AddressMode.DIRECT_PAGE_X_INDEX_INDIRECT, AddressMode.CONSTANT_8, AddressMode.DIRECT_PAGE_S_INDEX, - AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE, AddressMode.DIRECT_PAGE_LONG_INDIRECT, - AddressMode.IMPLIED, AddressMode.IMMEDIATE_M_FLAG_DEPENDENT, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.ADDRESS, AddressMode.LONG, - AddressMode.RELATIVE_8, AddressMode.DIRECT_PAGE_INDIRECT_Y_INDEX, AddressMode.DIRECT_PAGE_INDIRECT, AddressMode.DIRECT_PAGE_S_INDEX_INDIRECT_Y_INDEX, - AddressMode.ADDRESS, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_X_INDEX, AddressMode.DIRECT_PAGE_LONG_INDIRECT_Y_INDEX, - AddressMode.IMPLIED, AddressMode.ADDRESS_Y_INDEX, AddressMode.IMPLIED, AddressMode.IMPLIED, - AddressMode.ADDRESS_X_INDEX_INDIRECT, AddressMode.ADDRESS_X_INDEX, AddressMode.ADDRESS_X_INDEX, AddressMode.LONG_X_INDEX, - }; - } -} diff --git a/DiztinGUIsh/static/diz/Manager.cs b/DiztinGUIsh/static/diz/Manager.cs deleted file mode 100644 index 51a6d0cb..00000000 --- a/DiztinGUIsh/static/diz/Manager.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DiztinGUIsh -{ - public class Manager - { - public static int Step(int offset, bool branch, bool force, int prevOffset) - { - Project.unsavedChanges = true; - switch (Data.GetArchitechture(offset)) - { - case Data.Architechture.CPU65C816: return CPU65C816.Step(offset, branch, force, prevOffset); - case Data.Architechture.APUSPC700: return offset; - case Data.Architechture.GPUSuperFX: return offset; - } - return offset; - } - - public static int AutoStep(int offset, bool harsh, int amount) - { - Project.unsavedChanges = true; - int newOffset = offset, prevOffset = offset - 1, nextOffset = offset; - if (harsh) - { - while (newOffset < offset + amount) - { - nextOffset = Step(newOffset, false, true, prevOffset); - prevOffset = newOffset; - newOffset = nextOffset; - } - } else - { - Stack stack = new Stack(); - List seenBranches = new List(); - bool keepGoing = true; - - while (keepGoing) - { - switch (Data.GetArchitechture(newOffset)) - { - case Data.Architechture.CPU65C816: - if (seenBranches.Contains(newOffset)) - { - keepGoing = false; - break; - } - - int opcode = Data.GetROMByte(newOffset); - - nextOffset = Step(newOffset, false, false, prevOffset); - int jumpOffset = Step(newOffset, true, false, prevOffset); - - if (opcode == 0x40 || opcode == 0xCB || opcode == 0xDB || opcode == 0xF8 // RTI WAI STP SED - || opcode == 0xFB || opcode == 0x00 || opcode == 0x02 || opcode == 0x42 // XCE BRK COP WDM - || opcode == 0x6C || opcode == 0x7C || opcode == 0xDC || opcode == 0xFC // JMP JMP JML JSR - ) keepGoing = false; - - if (opcode == 0x4C || opcode == 0x5C || opcode == 0x80 || opcode == 0x82 // JMP JML BRA BRL - || opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 // BPL BMI BVC BVS - || opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0 // BCC BCS BNE BEQ - ) seenBranches.Add(newOffset); - - if (opcode == 0x08) // PHP - { - stack.Push(Data.GetMXFlags(newOffset)); - } else if (opcode == 0x28) // PLP - { - if (stack.Count == 0) - { - keepGoing = false; break; - } else - { - Data.SetMXFlags(newOffset, stack.Pop()); - } - } - - if (opcode == 0x60 || opcode == 0x6B) // RTS RTL - { - if (stack.Count == 0) - { - keepGoing = false; - break; - } - else - { - prevOffset = newOffset; - newOffset = stack.Pop(); - } - } else if (opcode == 0x20 || opcode == 0x22) // JSR JSL - { - stack.Push(nextOffset); - prevOffset = newOffset; - newOffset = jumpOffset; - } else - { - prevOffset = newOffset; - newOffset = nextOffset; - } - break; - case Data.Architechture.APUSPC700: - case Data.Architechture.GPUSuperFX: - nextOffset = Step(newOffset, false, true, prevOffset); - prevOffset = newOffset; - newOffset = nextOffset; - break; - } - - Data.FlagType flag = Data.GetFlag(newOffset); - if (!(flag == Data.FlagType.Unreached || flag == Data.FlagType.Opcode || flag == Data.FlagType.Operand)) keepGoing = false; - } - } - return newOffset; - } - - public static int Mark(int offset, Data.FlagType type, int count) - { - Project.unsavedChanges = true; - int i, size = Data.GetROMSize(); - for (i = 0; i < count && offset + i < size; i++) Data.SetFlag(offset + i, type); - return offset + i < size ? offset + i : size - 1; - } - - public static int MarkDataBank(int offset, int db, int count) - { - Project.unsavedChanges = true; - int i, size = Data.GetROMSize(); - for (i = 0; i < count && offset + i < size; i++) Data.SetDataBank(offset + i, db); - return offset + i < size ? offset + i : size - 1; - } - - public static int MarkDirectPage(int offset, int dp, int count) - { - Project.unsavedChanges = true; - int i, size = Data.GetROMSize(); - for (i = 0; i < count && offset + i < size; i++) Data.SetDirectPage(offset + i, dp); - return offset + i < size ? offset + i : size - 1; - } - - public static int MarkXFlag(int offset, bool x, int count) - { - Project.unsavedChanges = true; - int i, size = Data.GetROMSize(); - for (i = 0; i < count && offset + i < size; i++) Data.SetXFlag(offset + i, x); - return offset + i < size ? offset + i : size - 1; - } - - public static int MarkMFlag(int offset, bool m, int count) - { - Project.unsavedChanges = true; - int i, size = Data.GetROMSize(); - for (i = 0; i < count && offset + i < size; i++) Data.SetMFlag(offset + i, m); - return offset + i < size ? offset + i : size - 1; - } - - public static int MarkArchitechture(int offset, Data.Architechture arch, int count) - { - Project.unsavedChanges = true; - int i, size = Data.GetROMSize(); - for (i = 0; i < count && offset + i < size; i++) Data.SetArchitechture(offset + i, arch); - return offset + i < size ? offset + i : size - 1; - } - - public static int GetInstructionLength(int offset) - { - switch (Data.GetArchitechture(offset)) - { - case Data.Architechture.CPU65C816: return CPU65C816.GetInstructionLength(offset); - case Data.Architechture.APUSPC700: return 1; - case Data.Architechture.GPUSuperFX: return 1; - } - return 1; - } - - public static int FixMisalignedFlags() - { - int count = 0, size = Data.GetROMSize(); - - for (int i = 0; i < size; i++) - { - Data.FlagType flag = Data.GetFlag(i); - - if (flag == Data.FlagType.Opcode) - { - int len = GetInstructionLength(i); - for (int j = 1; j < len && i + j < size; j++) - { - if (Data.GetFlag(i + j) != Data.FlagType.Operand) - { - Data.SetFlag(i + j, Data.FlagType.Operand); - count++; - } - } - i += len - 1; - } else if (flag == Data.FlagType.Operand) - { - Data.SetFlag(i, Data.FlagType.Opcode); - count++; - i--; - } else if (Util.TypeStepSize(flag) > 1) - { - int step = Util.TypeStepSize(flag); - for (int j = 1; j < step; j++) - { - if (Data.GetFlag(i + j) != flag) - { - Data.SetFlag(i + j, flag); - count++; - } - } - i += step - 1; - } - } - - if (count > 0) Project.unsavedChanges = true; - - return count; - } - - public static void RescanInOutPoints() - { - for (int i = 0; i < Data.GetROMSize(); i++) Data.ClearInOutPoint(i); - - for (int i = 0; i < Data.GetROMSize(); i++) - { - if (Data.GetFlag(i) == Data.FlagType.Opcode) - { - switch (Data.GetArchitechture(i)) - { - case Data.Architechture.CPU65C816: CPU65C816.MarkInOutPoints(i); break; - case Data.Architechture.APUSPC700: break; - case Data.Architechture.GPUSuperFX: break; - } - } - } - - Project.unsavedChanges = true; - } - - public static void ImportBizHawkCDL(BizHawkCdl cdl) - { - if (!cdl.TryGetValue("CARTROM", out var cdlRomFlags)) - { - throw new InvalidDataException("The CDL file does not contain CARTROM block."); - } - - var size = Math.Min(cdlRomFlags.Count, Data.GetROMSize()); - bool m = false; - bool x = false; - for (var offset = 0; offset < size; offset++) - { - var cdlFlag = cdlRomFlags[offset]; - if (cdlFlag == BizHawkCdl.Flag.None) - continue; - - var type = Data.FlagType.Unreached; - if ((cdlFlag & BizHawkCdl.Flag.ExecFirst) != 0) - { - type = Data.FlagType.Opcode; - m = (cdlFlag & BizHawkCdl.Flag.CPUMFlag) != 0; - x = (cdlFlag & BizHawkCdl.Flag.CPUXFlag) != 0; - } - else if ((cdlFlag & BizHawkCdl.Flag.ExecOperand) != 0) - type = Data.FlagType.Operand; - else if ((cdlFlag & BizHawkCdl.Flag.CPUData) != 0) - type = Data.FlagType.Data8Bit; - else if ((cdlFlag & BizHawkCdl.Flag.DMAData) != 0) - type = Data.FlagType.Data8Bit; - Mark(offset, type, 1); - - if (type == Data.FlagType.Opcode || type == Data.FlagType.Operand) - { - // Operand reuses the last M and X flag values used in Opcode, - // since BizHawk CDL records M and X flags only in Opcode. - MarkMFlag(offset, m, 1); - MarkXFlag(offset, x, 1); - } - } - } - } -} diff --git a/DiztinGUIsh/util/GuiUtil.cs b/DiztinGUIsh/util/GuiUtil.cs new file mode 100644 index 00000000..40467f9b --- /dev/null +++ b/DiztinGUIsh/util/GuiUtil.cs @@ -0,0 +1,71 @@ +using System; +using System.ComponentModel; +using System.Windows.Forms; +using Diz.Core.util; + +namespace DiztinGUIsh.window +{ + public static class GuiUtil + { + public static void InvokeIfRequired(this ISynchronizeInvoke obj, MethodInvoker action) + { + if (obj.InvokeRequired) + { + var args = new object[0]; + obj.Invoke(action, args); + } + else + { + action(); + } + } + + public static string PromptToSelectFile(string initialDirectory = null) + { + var open = new OpenFileDialog {InitialDirectory = initialDirectory}; + return open.ShowDialog() == DialogResult.OK ? open.FileName : null; + } + + // prompt the user to confirm they'd like to do something. if yes, call 'confirmAction' + public static T PromptToConfirmAction(string promptSubject, string promptText, Func confirmAction) + { + var dialogResult = + MessageBox.Show(promptText, promptSubject, MessageBoxButtons.YesNo, MessageBoxIcon.Error); + + return dialogResult == DialogResult.Yes ? confirmAction() : default; + } + + /// + /// Generate data bindings so that a combobox control will populate from an enum list + /// Shortcut for doing these actions manually. + /// + /// + public static void BindListControlToEnum(ComboBox cb, object boundObject, string propertyName) + where TEnum : Enum + { + // I feel like there's gotta be a better way to do all this. But, I can live with this for now. + // + // future improvements: would be great if we didn't have to pass in a string, and instead passed + // in a property meta-info that we could generate the string value from. + + // take an enum type and create a list of each member, with the enum as the key, and anything in a [Description] attribute as the value + var enumValuesAndDescriptionsKvp = Util.GetEnumDescriptions(); + var bs = new BindingSource(enumValuesAndDescriptionsKvp, null); + + BindListControlToEnum(cb, boundObject, propertyName, bs); + } + + private static void BindListControlToEnum(ComboBox cb, object boundObject, string propertyName, + BindingSource bs) + { + cb.DataBindings.Add(new Binding( + "SelectedValue", boundObject, + propertyName, false, + DataSourceUpdateMode.OnPropertyChanged)); + + cb.DataSource = bs; + cb.DisplayMember = "Value"; + cb.ValueMember = "Key"; + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/util/LargeFilesReader.cs b/DiztinGUIsh/util/LargeFilesReader.cs new file mode 100644 index 00000000..e2d133cd --- /dev/null +++ b/DiztinGUIsh/util/LargeFilesReader.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Diz.Core.util; + +namespace DiztinGUIsh.util +{ + public class LargeFilesReader : ProgressBarWorker + { + public List Filenames { get; set; } + public Action LineReadCallback { get; set; } + + protected long SumFileLengthsInBytes { get; set; } + protected long BytesReadFromPreviousFiles { get; set; } + + protected override void Thread_DoWork() + { + SumFileLengthsInBytes = 0L; + foreach (var filename in Filenames) + { + SumFileLengthsInBytes += Util.GetFileSizeInBytes(filename); + } + + BytesReadFromPreviousFiles = 0L; + foreach (var filename in Filenames) + { + using var fs = File.Open(filename, FileMode.Open, FileAccess.Read); + using var bs = new BufferedStream(fs); + using var sr = new StreamReader(bs); + string line; + while ((line = sr.ReadLine()) != null) + { + LineReadCallback(line); + UpdateProgress(fs.Position); + } + + BytesReadFromPreviousFiles += fs.Length; + } + } + + private int previousProgress; + + protected void UpdateProgress(long currentPositionInBytes) + { + var percent = (BytesReadFromPreviousFiles + currentPositionInBytes) / (float)SumFileLengthsInBytes; + var progressValue = (int)(percent * 100); + + if (progressValue <= previousProgress) + return; + + // don't do this too often, kinda slow due to thread synchronization. + base.UpdateProgress(progressValue); + + previousProgress = progressValue; + } + public static void ReadFilesLines(string[] filenames, Action lineReadCallback) + { + var lfr = new LargeFilesReader() + { + Filenames = new List(filenames), + LineReadCallback = lineReadCallback, + }; + lfr.Run(); + } + } +} diff --git a/DiztinGUIsh/util/ProgressBarWorker.cs b/DiztinGUIsh/util/ProgressBarWorker.cs new file mode 100644 index 00000000..b9bfc757 --- /dev/null +++ b/DiztinGUIsh/util/ProgressBarWorker.cs @@ -0,0 +1,152 @@ +using System; +using System.Threading; +using DiztinGUIsh.window.dialog; + +namespace DiztinGUIsh +{ + // TODO: use https://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/ backgroundworker + public abstract class ProgressBarWorker + { + private ProgressDialog dialog; + private bool isRunning; + private Thread backgroundThread; + public bool IsMarquee { get; set; } + public string TextOverride { get; set; } + + protected void UpdateProgress(int i) + { + // i must be in range of 0 to 100 + dialog.UpdateProgress(i); + } + + protected abstract void Thread_DoWork(); + + // call from main thread to start a long-running job + // + // shows a progress bar dialog box while the work is being performed + // note: we're not being super-careful about thread safety here. + // if main thread is blocked it should be fine, but, if other things can + // still happen in the background, be really careful. + public void Run() + { + Setup(); + backgroundThread.Start(); + WaitForJobToFinish(); + } + + protected virtual void Setup() + { + if (isRunning) + throw new InvalidOperationException( + "Progress bar already running, existing job must finish first"); + + isRunning = true; + + dialog = new ProgressDialog(IsMarquee, TextOverride); + + // setup, but don't start, the new thread + backgroundThread = new Thread(Thread_Main); + + // honestly, not sure about this. works around some Invoke() stuff + backgroundThread.SetApartmentState(ApartmentState.STA); + } + + // blocking function + private void WaitForJobToFinish() + { + // blocks til worker thread closes this dialog box + dialog.ShowDialog(); + } + + // called from a new worker thread + private void Thread_Main() + { + try + { + // BAD APPROACH. we should instead get an event + // I'm too lazy right now. TODO FIXME + while (!dialog.Visible) + Thread.Sleep(50); + + Thread_DoWork(); + } + finally + { + isRunning = false; + SignalJobIsDone(); + } + } + + private void SignalJobIsDone() + { + // unblock the main thread from ShowDialog() + dialog?.BeginInvoke(new Action(() => dialog.Close())); + } + } +} + + + +namespace DiztinGUIsh +{ + public class ProgressBarJob : ProgressBarWorker + { + // a version that keeps calling 'callback' until it returns -1 + public static void Loop(long maxProgress, NextAction callback, string overrideTxt = null) + { + var j = new ProgressBarJob + { + MaxProgress = maxProgress, + Callback = callback, + IsMarquee = maxProgress == -1, + TextOverride = overrideTxt, + }; + j.Run(); + } + + // a version that calls action once and exits + // shows a "marquee" i.e. spinner + public static void RunAndWaitForCompletion(Action action, string overrideTxt = null) + { + Loop(-1, () => { + action(); + return -1; + }, overrideTxt); + } + + public NextAction Callback { get; set; } + public long MaxProgress { get; set; } + + protected override void Thread_DoWork() + { + UpdateProgress(0); + var progress = -1L; + do { + progress = Callback(); + UpdateProgress(progress); + } while (progress > 0); + } + + private int previousProgress; + + protected void UpdateProgress(long currentProgress) + { + if (MaxProgress <= 0) + return; + + var percent = currentProgress / (float)MaxProgress; + var progressValue = (int)(percent * 100); + + if (progressValue <= previousProgress) + return; + + // don't do this too often, kinda slow due to thread synchronization. + base.UpdateProgress(progressValue); + + previousProgress = progressValue; + } + + // return > 0 to continue. return value will be used to indicate progress in range of [0 -> MaxProgress] + public delegate long NextAction(); + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/AliasList.Designer.cs b/DiztinGUIsh/window/AliasList.Designer.cs index ef2b68b6..d147267e 100644 --- a/DiztinGUIsh/window/AliasList.Designer.cs +++ b/DiztinGUIsh/window/AliasList.Designer.cs @@ -28,10 +28,10 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle18 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle16 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle17 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle15 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); this.dataGridView1 = new System.Windows.Forms.DataGridView(); this.Address = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Alias = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -43,6 +43,7 @@ private void InitializeComponent() this.jump = new System.Windows.Forms.Button(); this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); + this.btnImportReplace = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); this.statusStrip1.SuspendLayout(); this.SuspendLayout(); @@ -58,14 +59,14 @@ private void InitializeComponent() this.Address, this.Alias, this.Comment}); - dataGridViewCellStyle18.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle18.BackColor = System.Drawing.SystemColors.Window; - dataGridViewCellStyle18.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - dataGridViewCellStyle18.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle18.SelectionBackColor = System.Drawing.Color.CornflowerBlue; - dataGridViewCellStyle18.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle18.WrapMode = System.Windows.Forms.DataGridViewTriState.False; - this.dataGridView1.DefaultCellStyle = dataGridViewCellStyle18; + dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle4.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle4.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle4.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle4.SelectionBackColor = System.Drawing.Color.CornflowerBlue; + dataGridViewCellStyle4.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.dataGridView1.DefaultCellStyle = dataGridViewCellStyle4; this.dataGridView1.Location = new System.Drawing.Point(0, 29); this.dataGridView1.Margin = new System.Windows.Forms.Padding(0); this.dataGridView1.Name = "dataGridView1"; @@ -88,8 +89,8 @@ private void InitializeComponent() // // Address // - dataGridViewCellStyle16.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Address.DefaultCellStyle = dataGridViewCellStyle16; + dataGridViewCellStyle1.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Address.DefaultCellStyle = dataGridViewCellStyle1; this.Address.HeaderText = "PC"; this.Address.MaxInputLength = 6; this.Address.Name = "Address"; @@ -97,8 +98,8 @@ private void InitializeComponent() // // Alias // - dataGridViewCellStyle17.Font = new System.Drawing.Font("Arial Narrow", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Alias.DefaultCellStyle = dataGridViewCellStyle17; + dataGridViewCellStyle2.Font = new System.Drawing.Font("Arial Narrow", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Alias.DefaultCellStyle = dataGridViewCellStyle2; this.Alias.HeaderText = "Label"; this.Alias.MaxInputLength = 60; this.Alias.Name = "Alias"; @@ -106,8 +107,8 @@ private void InitializeComponent() // // Comment // - dataGridViewCellStyle15.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Comment.DefaultCellStyle = dataGridViewCellStyle15; + dataGridViewCellStyle3.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Comment.DefaultCellStyle = dataGridViewCellStyle3; this.Comment.HeaderText = "Comment"; this.Comment.MaxInputLength = 800; this.Comment.Name = "Comment"; @@ -118,15 +119,15 @@ private void InitializeComponent() this.import.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.import.Location = new System.Drawing.Point(70, 3); this.import.Name = "import"; - this.import.Size = new System.Drawing.Size(62, 23); + this.import.Size = new System.Drawing.Size(116, 23); this.import.TabIndex = 1; - this.import.Text = "Import..."; + this.import.Text = "Import (Append) ..."; this.import.UseVisualStyleBackColor = true; - this.import.Click += new System.EventHandler(this.import_Click); + this.import.Click += new System.EventHandler(this.importAppend_Click); // // export // - this.export.Location = new System.Drawing.Point(136, 3); + this.export.Location = new System.Drawing.Point(338, 3); this.export.Name = "export"; this.export.Size = new System.Drawing.Size(62, 23); this.export.TabIndex = 2; @@ -167,12 +168,24 @@ private void InitializeComponent() // this.saveFileDialog1.Filter = "Comma Separated Value Files|*.csv|Text Files|*.txt|All Files|*.*"; // + // btnImportReplace + // + this.btnImportReplace.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnImportReplace.Location = new System.Drawing.Point(192, 3); + this.btnImportReplace.Name = "btnImportReplace"; + this.btnImportReplace.Size = new System.Drawing.Size(132, 23); + this.btnImportReplace.TabIndex = 5; + this.btnImportReplace.Text = "Import (Replace) ..."; + this.btnImportReplace.UseVisualStyleBackColor = true; + this.btnImportReplace.Click += new System.EventHandler(this.btnImportReplace_Click); + // // AliasList // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.import; this.ClientSize = new System.Drawing.Size(402, 364); + this.Controls.Add(this.btnImportReplace); this.Controls.Add(this.jump); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.export); @@ -186,7 +199,6 @@ private void InitializeComponent() this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Label List"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AliasList_FormClosing); - this.Load += new System.EventHandler(this.AliasList_Load); this.Resize += new System.EventHandler(this.AliasList_Resize); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); this.statusStrip1.ResumeLayout(false); @@ -209,5 +221,6 @@ private void InitializeComponent() private System.Windows.Forms.Button jump; private System.Windows.Forms.OpenFileDialog openFileDialog1; private System.Windows.Forms.SaveFileDialog saveFileDialog1; + private System.Windows.Forms.Button btnImportReplace; } } \ No newline at end of file diff --git a/DiztinGUIsh/window/AliasList.cs b/DiztinGUIsh/window/AliasList.cs index aabfc1e8..d73e27e7 100644 --- a/DiztinGUIsh/window/AliasList.cs +++ b/DiztinGUIsh/window/AliasList.cs @@ -1,153 +1,154 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; -using System.Data; -using System.Drawing; using System.Globalization; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Text.RegularExpressions; using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; +using DiztinGUIsh.controller; +using Label = Diz.Core.model.Label; namespace DiztinGUIsh.window { public partial class AliasList : Form { - // single instance - public static AliasList me; + private readonly MainWindow parentWindow; + private ProjectController ProjectController => parentWindow?.ProjectController; + private Data Data => ProjectController?.Project?.Data; - public bool locked = false; - private MainWindow mw; + public bool Locked; private int currentlyEditing = -1; - + public AliasList(MainWindow main) { - if (me == null) - { - me = this; - mw = main; - InitializeComponent(); - } - } - - private void AliasList_Load(object sender, EventArgs e) - { - + parentWindow = main; + InitializeComponent(); } private void AliasList_FormClosing(object sender, FormClosingEventArgs e) { - if (e.CloseReason == CloseReason.UserClosing) - { - e.Cancel = true; - this.Hide(); - } + if (e.CloseReason != CloseReason.UserClosing) return; + e.Cancel = true; + this.Hide(); } private void AliasList_Resize(object sender, EventArgs e) { - int h = Height - 68 - 22; + var h = Height - 68 - 22; dataGridView1.Height = h; } private void jump_Click(object sender, EventArgs e) { - if (int.TryParse((string)dataGridView1.SelectedRows[0].Cells[0].Value, NumberStyles.HexNumber, null, out int val)) + if (!int.TryParse((string) dataGridView1.SelectedRows[0].Cells[0].Value, NumberStyles.HexNumber, null, + out var val)) return; + + var offset = Data.ConvertSnesToPc(val); + if (offset >= 0) { - int offset = Util.ConvertSNEStoPC(val); - if (offset >= 0) - { - mw.SelectOffset(offset); - } + ProjectController.SelectOffset(offset); } } - private static void SplitOnFirstComma(string instr, out string first_part, out string remainder) + private static void SplitOnFirstComma(string instr, out string firstPart, out string remainder) { if (!instr.Contains(",")) { - first_part = instr; + firstPart = instr; remainder = ""; return; } - first_part = instr.Substring(0, instr.IndexOf(',')); + firstPart = instr.Substring(0, instr.IndexOf(',')); remainder = instr.Substring(instr.IndexOf(',') + 1); } - private void import_Click(object sender, EventArgs e) + private void ImportLabelsFromCsv(bool replaceAll) { - DialogResult result = openFileDialog1.ShowDialog(); - if (result != DialogResult.OK || openFileDialog1.FileName == "") + var result = openFileDialog1.ShowDialog(); + if (result != DialogResult.OK || openFileDialog1.FileName == "") return; - - int errLine = 0; + + var errLine = 0; try { - Dictionary newValues = new Dictionary(); - string[] lines = File.ReadAllLines(openFileDialog1.FileName); + var newValues = new Dictionary(); + var lines = Util.ReadLines(openFileDialog1.FileName).ToArray(); + + var validLabelChars = new Regex(@"^([a-zA-Z0-9_\-]*)$"); // NOTE: this is kind of a risky way to parse CSV files, won't deal with weirdness in the comments // section. - for (int i = 0; i < lines.Length; i++) { - var aliasInfo = new Data.AliasInfo(); + for (var i = 0; i < lines.Length; i++) + { + var label = new Label(); errLine = i + 1; - AliasList.SplitOnFirstComma(lines[i], out var labelAddress, out var remainder); - AliasList.SplitOnFirstComma(remainder, out aliasInfo.name, out aliasInfo.comment); + SplitOnFirstComma(lines[i], out var labelAddress, out var remainder); + SplitOnFirstComma(remainder, out label.Name, out label.Comment); - // todo (validate for valid label characters) - newValues.Add(int.Parse(labelAddress, NumberStyles.HexNumber, null), aliasInfo); + label.CleanUp(); + + label.Name = label.Name.Trim(); + if (!validLabelChars.Match(label.Name).Success) + throw new InvalidDataException("invalid label name: " + label.Name); + + newValues.Add(int.Parse(labelAddress, NumberStyles.HexNumber, null), label); } - foreach (KeyValuePair pair in newValues) + // everything read OK, modify the existing list now. point of no return + if (replaceAll) + Data.DeleteAllLabels(); + + ClearAndInvalidateDataGrid(); + + // this will call AddRow() to add items back to the UI datagrid. + foreach (var pair in newValues) { - pair.Value.CleanUp(); Data.AddLabel(pair.Key, pair.Value, true); } } - catch (Exception) + catch (Exception ex) { MessageBox.Show( - "An error occurred while parsing the file." + - (errLine > 0 ? string.Format(" (Check line {0}.)", errLine) : ""), + "An error occurred while parsing the file.\n" + ex.Message + + (errLine > 0 ? $" (Check line {errLine}.)" : ""), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void export_Click(object sender, EventArgs e) { - DialogResult result = saveFileDialog1.ShowDialog(); - if (result == DialogResult.OK && saveFileDialog1.FileName != "") + var result = saveFileDialog1.ShowDialog(); + if (result != DialogResult.OK || saveFileDialog1.FileName == "") return; + + try { - try - { - using (StreamWriter sw = new StreamWriter(saveFileDialog1.FileName)) - { - foreach (var pair in Data.GetAllLabels()) - { - sw.WriteLine( - $"{Util.NumberToBaseString(pair.Key, Util.NumberBase.Hexadecimal, 6)},{pair.Value.name},{pair.Value.comment}"); - } - } - } catch (Exception) + using var sw = new StreamWriter(saveFileDialog1.FileName); + foreach (var pair in Data.Labels) { - MessageBox.Show("An error occurred while saving the file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + sw.WriteLine( + $"{Util.NumberToBaseString(pair.Key, Util.NumberBase.Hexadecimal, 6)},{pair.Value.Name},{pair.Value.Comment}"); } + } catch (Exception) + { + MessageBox.Show("An error occurred while saving the file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void dataGridView1_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) { - if (int.TryParse((string)dataGridView1.Rows[e.Row.Index].Cells[0].Value, NumberStyles.HexNumber, null, out int val)) - { - locked = true; - Data.AddLabel(val, null, true); - locked = false; - mw.InvalidateTable(); - } + if (!int.TryParse((string) dataGridView1.Rows[e.Row.Index].Cells[0].Value, NumberStyles.HexNumber, null, + out var val)) return; + Locked = true; + Data.AddLabel(val, null, true); + Locked = false; + parentWindow.InvalidateTable(); // TODO: move to mainwindow, use notifychanged in mainwindow for this } private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) @@ -164,13 +165,13 @@ private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEv private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { if (dataGridView1.Rows[e.RowIndex].IsNewRow) return; - int val = -1, oldAddress = -1; - int.TryParse((string)dataGridView1.Rows[e.RowIndex].Cells[0].Value, NumberStyles.HexNumber, null, out oldAddress); + var val = -1; + int.TryParse((string)dataGridView1.Rows[e.RowIndex].Cells[0].Value, NumberStyles.HexNumber, null, out var oldAddress); - var labelAliasInfo = new Data.AliasInfo + var labelLabel = new Label { - name = (string) dataGridView1.Rows[e.RowIndex].Cells[1].Value, - comment = (string)dataGridView1.Rows[e.RowIndex].Cells[2].Value, + Name = (string) dataGridView1.Rows[e.RowIndex].Cells[1].Value, + Comment = (string)dataGridView1.Rows[e.RowIndex].Cells[2].Value, }; toolStripStatusLabel1.Text = ""; @@ -183,11 +184,11 @@ private void dataGridView1_CellValidating(object sender, DataGridViewCellValidat { e.Cancel = true; toolStripStatusLabel1.Text = "Must enter a valid hex address."; - } else if (oldAddress == -1 && Data.GetAllLabels().ContainsKey(val)) + } else if (oldAddress == -1 && Data.Labels.ContainsKey(val)) { e.Cancel = true; toolStripStatusLabel1.Text = "This address already has a label."; - var x = Data.GetAllLabels(); + Console.WriteLine(Util.NumberToBaseString(val, Util.NumberBase.Hexadecimal)); } else if (dataGridView1.EditingControl != null) { @@ -198,60 +199,136 @@ private void dataGridView1_CellValidating(object sender, DataGridViewCellValidat case 1: { val = oldAddress; - labelAliasInfo.name = e.FormattedValue.ToString(); + labelLabel.Name = e.FormattedValue.ToString(); // todo (validate for valid label characters) break; } case 2: { val = oldAddress; - labelAliasInfo.comment = e.FormattedValue.ToString(); + labelLabel.Comment = e.FormattedValue.ToString(); // todo (validate for valid comment characters, if any) break; } } - locked = true; + Locked = true; if (currentlyEditing >= 0) { if (val >= 0) Data.AddLabel(oldAddress, null, true); - Data.AddLabel(val, labelAliasInfo, true); + Data.AddLabel(val, labelLabel, true); } - locked = false; + Locked = false; currentlyEditing = -1; - mw.InvalidateTable(); + parentWindow.InvalidateTable(); // TODO: move to mainwindow, use notifychanged in mainwindow for this } - public void AddRow(int address, Data.AliasInfo alias) + public void AddRow(int address, Label alias) { - if (!locked) + if (Locked) + return; + RawAdd(address, alias); + dataGridView1.Invalidate(); + } + + private void RawAdd(int address, Label alias) + { + dataGridView1.Rows.Add(Util.NumberToBaseString(address, Util.NumberBase.Hexadecimal, 6), alias.Name, alias.Comment); + } + + public void RemoveRow(int address) + { + if (Locked) + return; + + for (var index = 0; index < dataGridView1.Rows.Count; index++) { - dataGridView1.Rows.Add(Util.NumberToBaseString(address, Util.NumberBase.Hexadecimal, 6), alias.name, alias.comment); + if ((string) dataGridView1.Rows[index].Cells[0].Value != + Util.NumberToBaseString(address, Util.NumberBase.Hexadecimal, 6)) continue; + + dataGridView1.Rows.RemoveAt(index); dataGridView1.Invalidate(); + break; } } - public void RemoveRow(int address) + public void ClearAndInvalidateDataGrid() { - if (!locked) + dataGridView1.Rows.Clear(); + dataGridView1.Invalidate(); + } + + private void importAppend_Click(object sender, EventArgs e) + { + if (MessageBox.Show("Info: Items in CSV will:\n" + + "1) CSV items will be added if their address doesn't already exist in this list\n" + + "2) CSV items will replace anything with the same address as items in the list\n" + + "3) any unmatched addresses in the list will be left alone\n" + + "\n" + + "Continue?\n", "Warning", MessageBoxButtons.OKCancel) != DialogResult.OK) + return; + + ImportLabelsFromCsv(false); + } + + private void btnImportReplace_Click(object sender, EventArgs e) + { + if (MessageBox.Show("Info: All list items will be deleted and replaced with the CSV file.\n" + + "\n" + + "Continue?\n", "Warning", MessageBoxButtons.OKCancel) != DialogResult.OK) + return; + + ImportLabelsFromCsv(true); + } + + public void RebindProject() + { + RepopulateFromData(); + + // todo: eventually use databinding/datasource, probably. + // Todo: modify observabledictionary wrapper to avoid having to do the .Dict call here. + Data.Labels.PropertyChanged += Labels_PropertyChanged; + Data.Labels.CollectionChanged += Labels_CollectionChanged; + } + + private void RepopulateFromData() + { + ClearAndInvalidateDataGrid(); + + if (Data == null) + return; + + // TODO: replace with winforms databinding eventually + foreach (var item in Data.Labels) + { + RawAdd(item.Key, item.Value); + } + dataGridView1.Invalidate(); + } + + private void Labels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.NewItems != null) { - for (int index = 0; index < dataGridView1.Rows.Count; index++) + foreach (KeyValuePair item in e.NewItems) { - if ((string)dataGridView1.Rows[index].Cells[0].Value == Util.NumberToBaseString(address, Util.NumberBase.Hexadecimal, 6)) - { - dataGridView1.Rows.RemoveAt(index); - dataGridView1.Invalidate(); - break; - } + AddRow(item.Key, item.Value); + } + } + + if (e.OldItems != null) + { + foreach (KeyValuePair item in e.OldItems) + { + RemoveRow(item.Key); } } } - public void Reset() + private void Labels_PropertyChanged(object sender, PropertyChangedEventArgs e) { - dataGridView1.Rows.Clear(); - dataGridView1.Invalidate(); + // if needed, catch any changes to label content here } } } diff --git a/DiztinGUIsh/window/DizDocument.cs b/DiztinGUIsh/window/DizDocument.cs new file mode 100644 index 00000000..e9669543 --- /dev/null +++ b/DiztinGUIsh/window/DizDocument.cs @@ -0,0 +1,36 @@ +using Diz.Core.model; +using DiztinGUIsh.Properties; + +namespace DiztinGUIsh.window +{ + // This class is for GUI internal use only, it should never be serialized. + // It represents the current "open document" (usually a thin wrapper around a Project) + // Anything that should persist should go inside Project instead. + // + // This can store some per-user settings that get saved locally. + // Don't save anything important here though. + public class DizDocument : DizDataModel + { + private Project project; + + public Project Project + { + get => project; + set => SetField(ref project, value, compareRefOnly: true); + } + + public string LastProjectFilename + { + get => Settings.Default.LastOpenedFile; + set + { + var lastOpenFile = Settings.Default.LastOpenedFile; + if (!SetField(ref lastOpenFile, value)) + return; + + Settings.Default.LastOpenedFile = lastOpenFile; + Settings.Default.Save(); + } + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.Actions.cs b/DiztinGUIsh/window/MainWindow.Actions.cs new file mode 100644 index 00000000..69f0d701 --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.Actions.cs @@ -0,0 +1,237 @@ +using Diz.Core.model; +using Diz.Core.util; +using DiztinGUIsh.Properties; + +namespace DiztinGUIsh.window +{ + public partial class MainWindow + { + private void OpenLastProject() + { + if (Document.LastProjectFilename == "") + return; + + // safeguard: if we crash opening this project, + // then next time we load make sure we don't try it again. + // this will be reset later + var projectToOpen = Document.LastProjectFilename; + Document.LastProjectFilename = ""; + + ProjectController.OpenProject(projectToOpen); + } + + private void OpenProject() + { + if (!PromptForOpenProjectFilename()) + return; + + ProjectController.OpenProject(openProjectFile.FileName); + } + + private void CreateNewProject() + { + if (!PromptContinueEvenIfUnsavedChanges()) + return; + + var romFilename = PromptForOpenFilename(); + if (romFilename == "") + return; + + ProjectController.ImportRomAndCreateNewProject(openFileDialog.FileName); + } + + private void ExportAssembly() + { + var adjustedSettings = PromptForExportSettingsAndConfirmation(); + if (!adjustedSettings.HasValue) + return; + + ProjectController.UpdateExportSettings(adjustedSettings.Value); + ProjectController.WriteAssemblyOutput(); + } + + + private void Step(int offset) + { + if (!RomDataPresent()) + return; + + ProjectController.MarkChanged(); + SelectOffset(Project.Data.Step(offset, false, false, offset - 1)); + UpdateUI_Tmp3(); + } + + private void StepIn(int offset) + { + if (!RomDataPresent()) + return; + + ProjectController.MarkChanged(); + SelectOffset(Project.Data.Step(offset, true, false, offset - 1)); + UpdateUI_Tmp3(); + } + + private void AutoStepSafe(int offset) + { + if (!RomDataPresent()) + return; + + ProjectController.MarkChanged(); + var destination = Project.Data.AutoStep(offset, false, 0); + if (moveWithStep) + SelectOffset(destination); + + UpdateUI_Tmp3(); + } + + private void AutoStepHarsh(int offset) + { + if (!RomDataPresent()) + return; + + if (!PromptHarshAutoStep(offset, out var newOffset, out var count)) + return; + + ProjectController.MarkChanged(); + var destination = Project.Data.AutoStep(newOffset, true, count); + + if (moveWithStep) + SelectOffset(destination); + + UpdateUI_Tmp3(); + } + + private void Mark(int offset) + { + if (!RomDataPresent()) + return; + + ProjectController.MarkChanged(); + var newOffset = Project.Data.MarkTypeFlag(offset, markFlag, RomUtil.GetByteLengthForFlag(markFlag)); + + SelectOffset(newOffset); + + UpdateUI_Tmp3(); + } + + private void MarkMany(int offset, int column) + { + if (!RomDataPresent()) + return; + + var mark = PromptMarkMany(offset, column); + if (mark == null) + return; + + MarkMany(mark.Property, mark.Start, mark.Value, mark.Count); + + UpdateSomeUI2(); + } + + private void MarkMany(int markProperty, int markStart, object markValue, int markCount) + { + var destination = markProperty switch + { + 0 => Project.Data.MarkTypeFlag(markStart, (Data.FlagType) markValue, markCount), + 1 => Project.Data.MarkDataBank(markStart, (int) markValue, markCount), + 2 => Project.Data.MarkDirectPage(markStart, (int) markValue, markCount), + 3 => Project.Data.MarkMFlag(markStart, (bool) markValue, markCount), + 4 => Project.Data.MarkXFlag(markStart, (bool) markValue, markCount), + 5 => Project.Data.MarkArchitecture(markStart, (Data.Architecture) markValue, markCount), + _ => 0 + }; + + ProjectController.MarkChanged(); + + if (moveWithStep) + SelectOffset(destination); + } + + private void GoToIntermediateAddress(int offset) + { + var snesOffset = FindIntermediateAddress(offset); + if (snesOffset == -1) + return; + + SelectOffset(snesOffset, 1); + } + + private void GoTo(int offset) + { + if (IsOffsetInRange(offset)) + SelectOffset(offset); + else + ShowOffsetOutOfRangeMsg(); + } + + private void GoToUnreached(bool end, bool direction) + { + if (!FindUnreached(SelectedOffset, end, direction, out var unreached)) + return; + + SelectOffset(unreached, 1); + } + + + private void FixMisalignedInstructions() + { + if (!PromptForMisalignmentCheck()) + return; + + var count = Project.Data.FixMisalignedFlags(); + + if (count > 0) + ProjectController.MarkChanged(); + InvalidateTable(); + + ShowInfo($"Modified {count} flags!", "Done!"); + } + + private void RescanForInOut() + { + if (!PromptForInOutChecking()) + return; + + Project.Data.RescanInOutPoints(); + ProjectController.MarkChanged(); + + InvalidateTable(); + ShowInfo("Scan complete!", "Done!"); + } + + private void SaveProject() + { + ProjectController.SaveProject(Project.ProjectFileName); + } + + private void ShowVisualizerForm() + { + visualForm ??= new VisualizerForm(this); + visualForm.Show(); + } + + private void ShowCommentList() + { + AliasList.Show(); + } + + private void SetMarkerLabel(Data.FlagType flagType) + { + markFlag = flagType; + UpdateMarkerLabel(); + } + + private void ToggleMoveWithStep() + { + moveWithStep = !moveWithStep; + moveWithStepToolStripMenuItem.Checked = moveWithStep; + } + + private void ToggleOpenLastProjectEnabled() + { + Settings.Default.OpenLastFileAutomatically = openLastProjectAutomaticallyToolStripMenuItem.Checked; + Settings.Default.Save(); + UpdateUiFromSettings(); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.Designer.cs b/DiztinGUIsh/window/MainWindow.Designer.cs index aa041aca..293ee1c9 100644 --- a/DiztinGUIsh/window/MainWindow.Designer.cs +++ b/DiztinGUIsh/window/MainWindow.Designer.cs @@ -1,4 +1,4 @@ -namespace DiztinGUIsh +namespace DiztinGUIsh.window { partial class MainWindow { @@ -61,12 +61,16 @@ private void InitializeComponent() this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.newProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripOpenLast = new System.Windows.Forms.ToolStripMenuItem(); this.saveProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveProjectAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.exportLogToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem(); + this.importUsageMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.importCDLToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.importTraceLogToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); + this.importTraceLogText = new System.Windows.Forms.ToolStripMenuItem(); + this.exportLogToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -118,6 +122,10 @@ private void InitializeComponent() this.binaryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.moveWithStepToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openLastProjectAutomaticallyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.labelListToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.importCaptureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.importTraceLogBinary = new System.Windows.Forms.ToolStripMenuItem(); this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.viewHelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.githubToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -126,14 +134,14 @@ private void InitializeComponent() this.percentComplete = new System.Windows.Forms.ToolStripStatusLabel(); this.seperator1 = new System.Windows.Forms.ToolStripStatusLabel(); this.currentMarker = new System.Windows.Forms.ToolStripStatusLabel(); - this.openROMFile = new System.Windows.Forms.OpenFileDialog(); + this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); this.openProjectFile = new System.Windows.Forms.OpenFileDialog(); this.saveProjectFile = new System.Windows.Forms.SaveFileDialog(); this.vScrollBar1 = new System.Windows.Forms.VScrollBar(); - this.saveLogSingleFile = new System.Windows.Forms.SaveFileDialog(); - this.chooseLogFolder = new System.Windows.Forms.FolderBrowserDialog(); + this.openUsageMapFile = new System.Windows.Forms.OpenFileDialog(); + this.openTraceLogDialog = new System.Windows.Forms.OpenFileDialog(); this.openCDLDialog = new System.Windows.Forms.OpenFileDialog(); - this.labelListToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.closeProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); ((System.ComponentModel.ISupportInitialize)(this.table)).BeginInit(); this.menuStrip1.SuspendLayout(); this.statusStrip1.SuspendLayout(); @@ -181,7 +189,7 @@ private void InitializeComponent() this.table.ShowCellToolTips = false; this.table.ShowEditingIcon = false; this.table.ShowRowErrors = false; - this.table.Size = new System.Drawing.Size(700, 500); + this.table.Size = new System.Drawing.Size(746, 492); this.table.TabIndex = 1; this.table.TabStop = false; this.table.VirtualMode = true; @@ -192,12 +200,14 @@ private void InitializeComponent() // ColumnAlias // dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight; - dataGridViewCellStyle1.Font = new System.Drawing.Font("Arial Narrow", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ColumnAlias.DefaultCellStyle = dataGridViewCellStyle1; this.ColumnAlias.HeaderText = "Label"; this.ColumnAlias.MaxInputLength = 60; + this.ColumnAlias.MinimumWidth = 6; this.ColumnAlias.Name = "ColumnAlias"; this.ColumnAlias.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.ColumnAlias.Width = 130; // // ColumnPC // @@ -206,10 +216,11 @@ private void InitializeComponent() this.ColumnPC.DefaultCellStyle = dataGridViewCellStyle2; this.ColumnPC.HeaderText = "PC"; this.ColumnPC.MaxInputLength = 6; + this.ColumnPC.MinimumWidth = 6; this.ColumnPC.Name = "ColumnPC"; this.ColumnPC.ReadOnly = true; this.ColumnPC.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.ColumnPC.Width = 45; + this.ColumnPC.Width = 58; // // ColumnChar // @@ -218,6 +229,7 @@ private void InitializeComponent() this.ColumnChar.DefaultCellStyle = dataGridViewCellStyle3; this.ColumnChar.HeaderText = "@"; this.ColumnChar.MaxInputLength = 1; + this.ColumnChar.MinimumWidth = 6; this.ColumnChar.Name = "ColumnChar"; this.ColumnChar.ReadOnly = true; this.ColumnChar.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; @@ -230,10 +242,11 @@ private void InitializeComponent() this.ColumnHex.DefaultCellStyle = dataGridViewCellStyle4; this.ColumnHex.HeaderText = "#"; this.ColumnHex.MaxInputLength = 3; + this.ColumnHex.MinimumWidth = 6; this.ColumnHex.Name = "ColumnHex"; this.ColumnHex.ReadOnly = true; this.ColumnHex.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.ColumnHex.Width = 20; + this.ColumnHex.Width = 26; // // ColumnPoints // @@ -242,9 +255,10 @@ private void InitializeComponent() this.ColumnPoints.DefaultCellStyle = dataGridViewCellStyle5; this.ColumnPoints.HeaderText = "<*>"; this.ColumnPoints.MaxInputLength = 3; + this.ColumnPoints.MinimumWidth = 6; this.ColumnPoints.Name = "ColumnPoints"; this.ColumnPoints.ReadOnly = true; - this.ColumnPoints.Width = 26; + this.ColumnPoints.Width = 34; // // ColumnInstruction // @@ -253,9 +267,11 @@ private void InitializeComponent() this.ColumnInstruction.DefaultCellStyle = dataGridViewCellStyle6; this.ColumnInstruction.HeaderText = "Instruction"; this.ColumnInstruction.MaxInputLength = 64; + this.ColumnInstruction.MinimumWidth = 6; this.ColumnInstruction.Name = "ColumnInstruction"; this.ColumnInstruction.ReadOnly = true; this.ColumnInstruction.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.ColumnInstruction.Width = 125; // // ColumnIA // @@ -264,19 +280,21 @@ private void InitializeComponent() this.ColumnIA.DefaultCellStyle = dataGridViewCellStyle7; this.ColumnIA.HeaderText = "IA"; this.ColumnIA.MaxInputLength = 6; + this.ColumnIA.MinimumWidth = 6; this.ColumnIA.Name = "ColumnIA"; this.ColumnIA.ReadOnly = true; this.ColumnIA.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.ColumnIA.Width = 45; + this.ColumnIA.Width = 58; // // ColumnFlag // - dataGridViewCellStyle8.Font = new System.Drawing.Font("Arial Narrow", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ColumnFlag.DefaultCellStyle = dataGridViewCellStyle8; this.ColumnFlag.HeaderText = "Flag"; + this.ColumnFlag.MinimumWidth = 6; this.ColumnFlag.Name = "ColumnFlag"; this.ColumnFlag.ReadOnly = true; - this.ColumnFlag.Width = 80; + this.ColumnFlag.Width = 86; // // ColumnDB // @@ -285,9 +303,10 @@ private void InitializeComponent() this.ColumnDB.DefaultCellStyle = dataGridViewCellStyle9; this.ColumnDB.HeaderText = "B"; this.ColumnDB.MaxInputLength = 2; + this.ColumnDB.MinimumWidth = 6; this.ColumnDB.Name = "ColumnDB"; this.ColumnDB.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.ColumnDB.Width = 20; + this.ColumnDB.Width = 26; // // ColumnDP // @@ -296,9 +315,10 @@ private void InitializeComponent() this.ColumnDP.DefaultCellStyle = dataGridViewCellStyle10; this.ColumnDP.HeaderText = "D"; this.ColumnDP.MaxInputLength = 4; + this.ColumnDP.MinimumWidth = 6; this.ColumnDP.Name = "ColumnDP"; this.ColumnDP.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.ColumnDP.Width = 32; + this.ColumnDP.Width = 42; // // ColumnM // @@ -307,9 +327,10 @@ private void InitializeComponent() this.ColumnM.DefaultCellStyle = dataGridViewCellStyle11; this.ColumnM.HeaderText = "M"; this.ColumnM.MaxInputLength = 2; + this.ColumnM.MinimumWidth = 6; this.ColumnM.Name = "ColumnM"; this.ColumnM.ReadOnly = true; - this.ColumnM.Width = 20; + this.ColumnM.Width = 26; // // ColumnX // @@ -318,22 +339,25 @@ private void InitializeComponent() this.ColumnX.DefaultCellStyle = dataGridViewCellStyle12; this.ColumnX.HeaderText = "X"; this.ColumnX.MaxInputLength = 2; + this.ColumnX.MinimumWidth = 6; this.ColumnX.Name = "ColumnX"; this.ColumnX.ReadOnly = true; - this.ColumnX.Width = 20; + this.ColumnX.Width = 26; // // ColumnComment // this.ColumnComment.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; dataGridViewCellStyle13.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle13.Font = new System.Drawing.Font("Arial Narrow", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle13.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ColumnComment.DefaultCellStyle = dataGridViewCellStyle13; this.ColumnComment.HeaderText = "Comment"; + this.ColumnComment.MinimumWidth = 6; this.ColumnComment.Name = "ColumnComment"; this.ColumnComment.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; // // menuStrip1 // + this.menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.fileToolStripMenuItem, this.editToolStripMenuItem, @@ -341,7 +365,7 @@ private void InitializeComponent() this.helpToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(717, 24); + this.menuStrip1.Size = new System.Drawing.Size(765, 24); this.menuStrip1.TabIndex = 0; this.menuStrip1.Text = "menuStrip1"; // @@ -350,12 +374,13 @@ private void InitializeComponent() this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.newProjectToolStripMenuItem, this.openProjectToolStripMenuItem, + this.toolStripOpenLast, + this.closeProjectToolStripMenuItem, this.saveProjectToolStripMenuItem, this.saveProjectAsToolStripMenuItem, this.toolStripSeparator1, + this.toolStripMenuItem2, this.exportLogToolStripMenuItem, - this.importCDLToolStripMenuItem, - this.importTraceLogToolStripMenuItem, this.toolStripSeparator7, this.exitToolStripMenuItem}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; @@ -378,6 +403,13 @@ private void InitializeComponent() this.openProjectToolStripMenuItem.Text = "Open Project..."; this.openProjectToolStripMenuItem.Click += new System.EventHandler(this.openProjectToolStripMenuItem_Click); // + // toolStripOpenLast + // + this.toolStripOpenLast.Name = "toolStripOpenLast"; + this.toolStripOpenLast.Size = new System.Drawing.Size(235, 22); + this.toolStripOpenLast.Text = "Open Last Project"; + this.toolStripOpenLast.Click += new System.EventHandler(this.toolStripOpenLast_Click); + // // saveProjectToolStripMenuItem // this.saveProjectToolStripMenuItem.Enabled = false; @@ -402,30 +434,55 @@ private void InitializeComponent() this.toolStripSeparator1.Name = "toolStripSeparator1"; this.toolStripSeparator1.Size = new System.Drawing.Size(232, 6); // - // exportLogToolStripMenuItem + // toolStripMenuItem2 // - this.exportLogToolStripMenuItem.Enabled = false; - this.exportLogToolStripMenuItem.Name = "exportLogToolStripMenuItem"; - this.exportLogToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.E))); - this.exportLogToolStripMenuItem.Size = new System.Drawing.Size(235, 22); - this.exportLogToolStripMenuItem.Text = "Export Disassembly..."; - this.exportLogToolStripMenuItem.Click += new System.EventHandler(this.exportLogToolStripMenuItem_Click); + this.toolStripMenuItem2.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.importUsageMapToolStripMenuItem, + this.importCDLToolStripMenuItem, + this.toolStripSeparator8, + this.importTraceLogText}); + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(235, 22); + this.toolStripMenuItem2.Text = "Import"; + // + // importUsageMapToolStripMenuItem + // + this.importUsageMapToolStripMenuItem.Enabled = false; + this.importUsageMapToolStripMenuItem.Name = "importUsageMapToolStripMenuItem"; + this.importUsageMapToolStripMenuItem.Size = new System.Drawing.Size(218, 22); + this.importUsageMapToolStripMenuItem.Text = "Import BSNES Usage Map..."; + this.importUsageMapToolStripMenuItem.Click += new System.EventHandler(this.importUsageMapToolStripMenuItem_Click_1); // // importCDLToolStripMenuItem // this.importCDLToolStripMenuItem.Enabled = false; this.importCDLToolStripMenuItem.Name = "importCDLToolStripMenuItem"; this.importCDLToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.I))); - this.importCDLToolStripMenuItem.Size = new System.Drawing.Size(235, 22); + this.importCDLToolStripMenuItem.Size = new System.Drawing.Size(218, 22); this.importCDLToolStripMenuItem.Text = "Import CDL..."; - this.importCDLToolStripMenuItem.Click += new System.EventHandler(this.importCDLToolStripMenuItem_Click); + this.importCDLToolStripMenuItem.Click += new System.EventHandler(this.importCDLToolStripMenuItem_Click_1); + // + // toolStripSeparator8 + // + this.toolStripSeparator8.Name = "toolStripSeparator8"; + this.toolStripSeparator8.Size = new System.Drawing.Size(215, 6); + // + // importTraceLogText + // + this.importTraceLogText.Enabled = false; + this.importTraceLogText.Name = "importTraceLogText"; + this.importTraceLogText.Size = new System.Drawing.Size(218, 22); + this.importTraceLogText.Text = "Import BSNES Trace Log..."; + this.importTraceLogText.Click += new System.EventHandler(this.importBsnesTracelogText_Click); // - // importTraceLogToolStripMenuItem + // exportLogToolStripMenuItem // - this.importTraceLogToolStripMenuItem.Enabled = false; - this.importTraceLogToolStripMenuItem.Name = "importTraceLogToolStripMenuItem"; - this.importTraceLogToolStripMenuItem.Size = new System.Drawing.Size(235, 22); - this.importTraceLogToolStripMenuItem.Text = "Import Trace Log..."; + this.exportLogToolStripMenuItem.Enabled = false; + this.exportLogToolStripMenuItem.Name = "exportLogToolStripMenuItem"; + this.exportLogToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.E))); + this.exportLogToolStripMenuItem.Size = new System.Drawing.Size(235, 22); + this.exportLogToolStripMenuItem.Text = "Export Disassembly..."; + this.exportLogToolStripMenuItem.Click += new System.EventHandler(this.exportLogToolStripMenuItem_Click); // // toolStripSeparator7 // @@ -780,14 +837,14 @@ private void InitializeComponent() this.graphicsWindowToolStripMenuItem, this.constantsToolStripMenuItem, this.optionsToolStripMenuItem, - this.labelListToolStripMenuItem}); + this.labelListToolStripMenuItem, + this.importCaptureToolStripMenuItem}); this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; this.viewToolStripMenuItem.Size = new System.Drawing.Size(46, 20); this.viewToolStripMenuItem.Text = "&Tools"; // // visualMapToolStripMenuItem // - this.visualMapToolStripMenuItem.Enabled = false; this.visualMapToolStripMenuItem.Name = "visualMapToolStripMenuItem"; this.visualMapToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V))); this.visualMapToolStripMenuItem.Size = new System.Drawing.Size(212, 22); @@ -842,7 +899,8 @@ private void InitializeComponent() // optionsToolStripMenuItem // this.optionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.moveWithStepToolStripMenuItem}); + this.moveWithStepToolStripMenuItem, + this.openLastProjectAutomaticallyToolStripMenuItem}); this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; this.optionsToolStripMenuItem.Size = new System.Drawing.Size(212, 22); this.optionsToolStripMenuItem.Text = "Options"; @@ -852,10 +910,41 @@ private void InitializeComponent() this.moveWithStepToolStripMenuItem.Checked = true; this.moveWithStepToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; this.moveWithStepToolStripMenuItem.Name = "moveWithStepToolStripMenuItem"; - this.moveWithStepToolStripMenuItem.Size = new System.Drawing.Size(158, 22); + this.moveWithStepToolStripMenuItem.Size = new System.Drawing.Size(244, 22); this.moveWithStepToolStripMenuItem.Text = "Move With Step"; this.moveWithStepToolStripMenuItem.Click += new System.EventHandler(this.moveWithStepToolStripMenuItem_Click); // + // openLastProjectAutomaticallyToolStripMenuItem + // + this.openLastProjectAutomaticallyToolStripMenuItem.Name = "openLastProjectAutomaticallyToolStripMenuItem"; + this.openLastProjectAutomaticallyToolStripMenuItem.Size = new System.Drawing.Size(244, 22); + this.openLastProjectAutomaticallyToolStripMenuItem.Text = "Open Last Project Automatically"; + this.openLastProjectAutomaticallyToolStripMenuItem.Click += new System.EventHandler(this.openLastProjectAutomaticallyToolStripMenuItem_Click); + // + // labelListToolStripMenuItem + // + this.labelListToolStripMenuItem.Enabled = false; + this.labelListToolStripMenuItem.Name = "labelListToolStripMenuItem"; + this.labelListToolStripMenuItem.Size = new System.Drawing.Size(212, 22); + this.labelListToolStripMenuItem.Text = "Label List"; + this.labelListToolStripMenuItem.Click += new System.EventHandler(this.labelListToolStripMenuItem_Click); + // + // importCaptureToolStripMenuItem + // + this.importCaptureToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.importTraceLogBinary}); + this.importCaptureToolStripMenuItem.Name = "importCaptureToolStripMenuItem"; + this.importCaptureToolStripMenuItem.Size = new System.Drawing.Size(212, 22); + this.importCaptureToolStripMenuItem.Text = "Live Capture"; + // + // importTraceLogBinary + // + this.importTraceLogBinary.Enabled = false; + this.importTraceLogBinary.Name = "importTraceLogBinary"; + this.importTraceLogBinary.Size = new System.Drawing.Size(207, 22); + this.importTraceLogBinary.Text = "BSNESPlus Trace Logging"; + this.importTraceLogBinary.Click += new System.EventHandler(this.importTraceLogBinary_Click); + // // helpToolStripMenuItem // this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -890,13 +979,15 @@ private void InitializeComponent() // // statusStrip1 // + this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.percentComplete, this.seperator1, this.currentMarker}); - this.statusStrip1.Location = new System.Drawing.Point(0, 524); + this.statusStrip1.Location = new System.Drawing.Point(0, 516); this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(717, 22); + this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 15, 0); + this.statusStrip1.Size = new System.Drawing.Size(765, 22); this.statusStrip1.TabIndex = 3; this.statusStrip1.Text = "statusStrip1"; // @@ -919,59 +1010,60 @@ private void InitializeComponent() this.currentMarker.Size = new System.Drawing.Size(110, 17); this.currentMarker.Text = "Marker: Data (8-bit)"; // - // openROMFile + // openFileDialog // - this.openROMFile.Filter = "SNES ROM Images|*.smc;*.sfc|All files|*.*"; + this.openFileDialog.Filter = "SNES ROM Images|*.smc;*.sfc|All files|*.*"; // // openProjectFile // - this.openProjectFile.Filter = "DiztinGUIsh Project Files|*.diz|DiztinGUIsh Project Files (Uncompressed)|*.dizraw" + - "|All Files|*.*"; + this.openProjectFile.Filter = "DiztinGUIsh Project Files|*.diz;*.dizraw|All Files|*.*"; // // saveProjectFile // - this.saveProjectFile.Filter = "DiztinGUIsh Project Files|*.diz|DiztinGUIsh Project Files (Uncompressed)|*.dizraw" + - "|All Files|*.*"; + this.saveProjectFile.Filter = "DiztinGUIsh Project Files|*.diz;*.dizraw|All Files|*.*"; this.saveProjectFile.Title = "New Project.diz"; // // vScrollBar1 // this.vScrollBar1.Enabled = false; - this.vScrollBar1.Location = new System.Drawing.Point(700, 24); + this.vScrollBar1.Location = new System.Drawing.Point(746, 24); this.vScrollBar1.Maximum = 32768; this.vScrollBar1.Name = "vScrollBar1"; - this.vScrollBar1.Size = new System.Drawing.Size(17, 500); + this.vScrollBar1.Size = new System.Drawing.Size(17, 492); this.vScrollBar1.TabIndex = 2; this.vScrollBar1.ValueChanged += new System.EventHandler(this.vScrollBar1_ValueChanged); // - // saveLogSingleFile + // openUsageMapFile // - this.saveLogSingleFile.Filter = "Assembly Files|*.asm|All Files|*.*"; + this.openUsageMapFile.Filter = "bsnes-plus usage map files|*.bin"; // - // labelListToolStripMenuItem + // openTraceLogDialog // - this.labelListToolStripMenuItem.Enabled = false; - this.labelListToolStripMenuItem.Name = "labelListToolStripMenuItem"; - this.labelListToolStripMenuItem.Size = new System.Drawing.Size(212, 22); - this.labelListToolStripMenuItem.Text = "Label List"; - this.labelListToolStripMenuItem.Click += new System.EventHandler(this.labelListToolStripMenuItem_Click); + this.openTraceLogDialog.Filter = "bsnes-plus trace log|*.log"; // // openCDLDialog // this.openCDLDialog.Filter = "BizHawk Code Data Logger Files|*.cdl|All Files|*.*"; // + // closeProjectToolStripMenuItem + // + this.closeProjectToolStripMenuItem.Name = "closeProjectToolStripMenuItem"; + this.closeProjectToolStripMenuItem.Size = new System.Drawing.Size(235, 22); + this.closeProjectToolStripMenuItem.Text = "Close Project"; + this.closeProjectToolStripMenuItem.Click += new System.EventHandler(this.closeProjectToolStripMenuItem_Click); + // // MainWindow // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(717, 546); + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(765, 538); this.Controls.Add(this.vScrollBar1); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.table); this.Controls.Add(this.menuStrip1); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MainMenuStrip = this.menuStrip1; - this.MinimumSize = new System.Drawing.Size(733, 200); + this.MinimumSize = new System.Drawing.Size(780, 196); this.Name = "MainWindow"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "DiztinGUIsh"; @@ -1001,7 +1093,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem saveProjectAsToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem exportLogToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem importCDLToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem stepOverToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem stepInToolStripMenuItem; @@ -1051,7 +1142,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem viewHelpToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; private System.Windows.Forms.ToolStripStatusLabel percentComplete; - private System.Windows.Forms.OpenFileDialog openROMFile; + private System.Windows.Forms.OpenFileDialog openFileDialog; private System.Windows.Forms.OpenFileDialog openProjectFile; private System.Windows.Forms.SaveFileDialog saveProjectFile; private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; @@ -1061,10 +1152,10 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripStatusLabel currentMarker; private System.Windows.Forms.ToolStripMenuItem rescanForInOutPointsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem githubToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem importTraceLogToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem gotoNextUnreachedToolStripMenuItem; - private System.Windows.Forms.SaveFileDialog saveLogSingleFile; - private System.Windows.Forms.FolderBrowserDialog chooseLogFolder; + private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem moveWithStepToolStripMenuItem; + private System.Windows.Forms.OpenFileDialog openUsageMapFile; private System.Windows.Forms.DataGridViewTextBoxColumn ColumnAlias; private System.Windows.Forms.DataGridViewTextBoxColumn ColumnPC; private System.Windows.Forms.DataGridViewTextBoxColumn ColumnChar; @@ -1078,10 +1169,19 @@ private void InitializeComponent() private System.Windows.Forms.DataGridViewTextBoxColumn ColumnM; private System.Windows.Forms.DataGridViewTextBoxColumn ColumnX; private System.Windows.Forms.DataGridViewTextBoxColumn ColumnComment; - private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem moveWithStepToolStripMenuItem; + public System.Windows.Forms.OpenFileDialog openTraceLogDialog; private System.Windows.Forms.ToolStripMenuItem labelListToolStripMenuItem; private System.Windows.Forms.OpenFileDialog openCDLDialog; + private System.Windows.Forms.ToolStripMenuItem toolStripOpenLast; + private System.Windows.Forms.ToolStripMenuItem openLastProjectAutomaticallyToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem importUsageMapToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem importCDLToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator8; + private System.Windows.Forms.ToolStripMenuItem importTraceLogText; + private System.Windows.Forms.ToolStripMenuItem importCaptureToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem importTraceLogBinary; + private System.Windows.Forms.ToolStripMenuItem closeProjectToolStripMenuItem; } } diff --git a/DiztinGUIsh/window/MainWindow.Importers.cs b/DiztinGUIsh/window/MainWindow.Importers.cs new file mode 100644 index 00000000..28df36bc --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.Importers.cs @@ -0,0 +1,69 @@ +using System; +using System.Windows.Forms; +using DiztinGUIsh.window.dialog; + +namespace DiztinGUIsh.window +{ + public partial class MainWindow + { + public IImportRomDialogView GetImportView() + { + return new ImportRomDialog(); + } + + private void ImportBizhawkCDL() + { + var filename = PromptOpenBizhawkCDLFile(); + if (filename != null && filename == "") return; + ImportBizHawkCdl(filename); + UpdateSomeUI2(); + } + + private void ImportBizHawkCdl(string filename) + { + try + { + ProjectController.ImportBizHawkCdl(filename); + } + catch (Exception ex) + { + ShowError(ex.Message, "Error"); + } + } + + private void ImportBsnesTraceLogText() + { + if (!PromptForImportBSNESTraceLogFile()) return; + var (numModifiedFlags, numFiles) = ImportBSNESTraceLogs(); + ReportNumberFlagsModified(numModifiedFlags, numFiles); + } + + private void ImportBSNESUsageMap() + { + if (openUsageMapFile.ShowDialog() != DialogResult.OK) + return; + + var numModifiedFlags = ProjectController.ImportBsnesUsageMap(openUsageMapFile.FileName); + + ShowInfo($"Modified total {numModifiedFlags} flags!", "Done"); + } + + private (long numBytesModified, int numFiles) ImportBSNESTraceLogs() + { + var numBytesModified = ProjectController.ImportBsnesTraceLogs(openTraceLogDialog.FileNames); + return (numBytesModified, openTraceLogDialog.FileNames.Length); + } + + private void ImportBsnesBinaryTraceLog() + { + new BsnesTraceLogBinaryMonitorForm(this).ShowDialog(); + RefreshUi(); + } + + private void OnImportedProjectSuccess() + { + UpdateSaveOptionStates(saveEnabled: false, saveAsEnabled: true, closeEnabled: true); + RefreshUi(); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.MainTable.cs b/DiztinGUIsh/window/MainWindow.MainTable.cs new file mode 100644 index 00000000..f35de76c --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.MainTable.cs @@ -0,0 +1,380 @@ +using System; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; + +namespace DiztinGUIsh.window +{ + // Everything in here should probably go in its own usercontrol for JUST the table. + // It's a complicated little beast. + public partial class MainWindow + { + // Data offset of the selected row + public int SelectedOffset => table.CurrentCell.RowIndex + ViewOffset; + + private int rowsToShow; + private bool moveWithStep = true; + + public void InvalidateTable() => table.Invalidate(); + + private void ScrollTableBy(int delta) + { + if (Project?.Data == null || Project.Data.GetRomSize() <= 0) + return; + int selRow = table.CurrentCell.RowIndex + ViewOffset, selCol = table.CurrentCell.ColumnIndex; + var amount = delta / 0x18; + ViewOffset -= amount; + UpdateDataGridView(); + if (selRow < ViewOffset) selRow = ViewOffset; + else if (selRow >= ViewOffset + rowsToShow) selRow = ViewOffset + rowsToShow - 1; + table.CurrentCell = table.Rows[selRow - ViewOffset].Cells[selCol]; + InvalidateTable(); + } + + private void vScrollBar1_ValueChanged(object sender, EventArgs e) + { + if (table.CurrentCell == null) + return; + + int selOffset = table.CurrentCell.RowIndex + ViewOffset; + ViewOffset = vScrollBar1.Value; + UpdateDataGridView(); + + if (selOffset < ViewOffset) table.CurrentCell = table.Rows[0].Cells[table.CurrentCell.ColumnIndex]; + else if (selOffset >= ViewOffset + rowsToShow) + table.CurrentCell = table.Rows[rowsToShow - 1].Cells[table.CurrentCell.ColumnIndex]; + else table.CurrentCell = table.Rows[selOffset - ViewOffset].Cells[table.CurrentCell.ColumnIndex]; + + InvalidateTable(); + } + + private void table_MouseDown(object sender, MouseEventArgs e) + { + InvalidateTable(); + } + + private void table_KeyDown(object sender, KeyEventArgs e) + { + if (Project?.Data == null || Project.Data.GetRomSize() <= 0) return; + + var offset = table.CurrentCell.RowIndex + ViewOffset; + var newOffset = offset; + var amount = 0x01; + + Console.WriteLine(e.KeyCode); + switch (e.KeyCode) + { + case Keys.Home: + case Keys.PageUp: + case Keys.Up: + amount = e.KeyCode == Keys.Up ? 0x01 : e.KeyCode == Keys.PageUp ? 0x10 : 0x100; + newOffset = offset - amount; + if (newOffset < 0) newOffset = 0; + SelectOffset(newOffset); + break; + case Keys.End: + case Keys.PageDown: + case Keys.Down: + amount = e.KeyCode == Keys.Down ? 0x01 : e.KeyCode == Keys.PageDown ? 0x10 : 0x100; + newOffset = offset + amount; + if (newOffset >= Project.Data.GetRomSize()) newOffset = Project.Data.GetRomSize() - 1; + SelectOffset(newOffset); + break; + case Keys.Left: + amount = table.CurrentCell.ColumnIndex; + amount = amount - 1 < 0 ? 0 : amount - 1; + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[amount]; + break; + case Keys.Right: + amount = table.CurrentCell.ColumnIndex; + amount = amount + 1 >= table.ColumnCount ? table.ColumnCount - 1 : amount + 1; + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[amount]; + break; + case Keys.S: + Step(offset); + break; + case Keys.I: + StepIn(offset); + break; + case Keys.A: + AutoStepSafe(offset); + break; + case Keys.T: + GoToIntermediateAddress(offset); + break; + case Keys.U: + GoToUnreached(true, true); + break; + case Keys.H: + GoToUnreached(false, false); + break; + case Keys.N: + GoToUnreached(false, true); + break; + case Keys.K: + Mark(offset); + break; + case Keys.L: + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[0]; + table.BeginEdit(true); + break; + case Keys.B: + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[8]; + table.BeginEdit(true); + break; + case Keys.D: + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[9]; + table.BeginEdit(true); + break; + case Keys.M: + Project.Data.SetMFlag(offset, !Project.Data.GetMFlag(offset)); + break; + case Keys.X: + Project.Data.SetXFlag(offset, !Project.Data.GetXFlag(offset)); + break; + case Keys.C: + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[12]; + table.BeginEdit(true); + break; + } + + e.Handled = true; + InvalidateTable(); + } + + private void table_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) + { + var row = e.RowIndex + ViewOffset; + if (row >= Project.Data.GetRomSize()) return; + switch (e.ColumnIndex) + { + case 0: + e.Value = Project.Data.GetLabelName(Project.Data.ConvertPCtoSnes(row)); + break; + case 1: + e.Value = Util.NumberToBaseString(Project.Data.ConvertPCtoSnes(row), Util.NumberBase.Hexadecimal, 6); + break; + case 2: + e.Value = (char)Project.Data.GetRomByte(row); + break; + case 3: + e.Value = Util.NumberToBaseString(Project.Data.GetRomByte(row), displayBase); + break; + case 4: + e.Value = RomUtil.PointToString(Project.Data.GetInOutPoint(row)); + break; + case 5: + var len = Project.Data.GetInstructionLength(row); + e.Value = row + len <= Project.Data.GetRomSize() ? Project.Data.GetInstruction(row) : ""; + break; + case 6: + var ia = Project.Data.GetIntermediateAddressOrPointer(row); + e.Value = ia >= 0 ? Util.NumberToBaseString(ia, Util.NumberBase.Hexadecimal, 6) : ""; + break; + case 7: + e.Value = Util.GetEnumDescription(Project.Data.GetFlag(row)); + break; + case 8: + e.Value = Util.NumberToBaseString(Project.Data.GetDataBank(row), Util.NumberBase.Hexadecimal, 2); + break; + case 9: + e.Value = Util.NumberToBaseString(Project.Data.GetDirectPage(row), Util.NumberBase.Hexadecimal, 4); + break; + case 10: + e.Value = RomUtil.BoolToSize(Project.Data.GetMFlag(row)); + break; + case 11: + e.Value = RomUtil.BoolToSize(Project.Data.GetXFlag(row)); + break; + case 12: + e.Value = Project.Data.GetComment(Project.Data.ConvertPCtoSnes(row)); + break; + } + } + + private void table_CellValuePushed(object sender, DataGridViewCellValueEventArgs e) + { + string value = e.Value as string; + int result; + int row = e.RowIndex + ViewOffset; + if (row >= Project.Data.GetRomSize()) return; + switch (e.ColumnIndex) + { + case 0: + Project.Data.AddLabel(Project.Data.ConvertPCtoSnes(row), new Diz.Core.model.Label() { Name = value }, true); + break; // todo (validate for valid label characters) + case 8: + if (int.TryParse(value, NumberStyles.HexNumber, null, out result)) Project.Data.SetDataBank(row, result); + break; + case 9: + if (int.TryParse(value, NumberStyles.HexNumber, null, out result)) Project.Data.SetDirectPage(row, result); + break; + case 10: + Project.Data.SetMFlag(row, (value == "8" || value == "M")); + break; + case 11: + Project.Data.SetXFlag(row, (value == "8" || value == "X")); + break; + case 12: + Project.Data.AddComment(Project.Data.ConvertPCtoSnes(row), value, true); + break; + } + + table.InvalidateRow(e.RowIndex); + } + + public void PaintCell(int offset, DataGridViewCellStyle style, int column, int selOffset) + { + // editable cells show up green + if (column == 0 || column == 8 || column == 9 || column == 12) style.SelectionBackColor = Color.Chartreuse; + + switch (Project.Data.GetFlag(offset)) + { + case Data.FlagType.Unreached: + style.BackColor = Color.LightGray; + style.ForeColor = Color.DarkSlateGray; + break; + case Data.FlagType.Opcode: + int opcode = Project.Data.GetRomByte(offset); + switch (column) + { + case 4: // <*> + Data.InOutPoint point = Project.Data.GetInOutPoint(offset); + int r = 255, g = 255, b = 255; + if ((point & (Data.InOutPoint.EndPoint | Data.InOutPoint.OutPoint)) != 0) g -= 50; + if ((point & (Data.InOutPoint.InPoint)) != 0) r -= 50; + if ((point & (Data.InOutPoint.ReadPoint)) != 0) b -= 50; + style.BackColor = Color.FromArgb(r, g, b); + break; + case 5: // Instruction + if (opcode == 0x40 || opcode == 0xCB || opcode == 0xDB || opcode == 0xF8 // RTI WAI STP SED + || opcode == 0xFB || opcode == 0x00 || opcode == 0x02 || opcode == 0x42 // XCE BRK COP WDM + ) style.BackColor = Color.Yellow; + break; + case 8: // Data Bank + if (opcode == 0xAB || opcode == 0x44 || opcode == 0x54) // PLB MVP MVN + style.BackColor = Color.OrangeRed; + else if (opcode == 0x8B) // PHB + style.BackColor = Color.Yellow; + break; + case 9: // Direct Page + if (opcode == 0x2B || opcode == 0x5B) // PLD TCD + style.BackColor = Color.OrangeRed; + if (opcode == 0x0B || opcode == 0x7B) // PHD TDC + style.BackColor = Color.Yellow; + break; + case 10: // M Flag + case 11: // X Flag + int mask = column == 10 ? 0x20 : 0x10; + if (opcode == 0x28 || ((opcode == 0xC2 || opcode == 0xE2) // PLP SEP REP + && (Project.Data.GetRomByte(offset + 1) & mask) != 0)) // relevant bit set + style.BackColor = Color.OrangeRed; + if (opcode == 0x08) // PHP + style.BackColor = Color.Yellow; + break; + } + break; + case Data.FlagType.Operand: + style.ForeColor = Color.LightGray; + break; + case Data.FlagType.Graphics: + style.BackColor = Color.LightPink; + break; + case Data.FlagType.Music: + style.BackColor = Color.PowderBlue; + break; + case Data.FlagType.Data8Bit: + case Data.FlagType.Data16Bit: + case Data.FlagType.Data24Bit: + case Data.FlagType.Data32Bit: + style.BackColor = Color.NavajoWhite; + break; + case Data.FlagType.Pointer16Bit: + case Data.FlagType.Pointer24Bit: + case Data.FlagType.Pointer32Bit: + style.BackColor = Color.Orchid; + break; + case Data.FlagType.Text: + style.BackColor = Color.Aquamarine; + break; + case Data.FlagType.Empty: + style.BackColor = Color.DarkSlateGray; + style.ForeColor = Color.LightGray; + break; + } + + if (selOffset >= 0 && selOffset < Project.Data.GetRomSize()) + { + if (column == 1 + //&& (Project.Data.GetFlag(selOffset) == Data.FlagType.Opcode || Project.Data.GetFlag(selOffset) == Data.FlagType.Unreached) + && Project.Data.ConvertSnesToPc(Project.Data.GetIntermediateAddressOrPointer(selOffset)) == offset + ) style.BackColor = Color.DeepPink; + + if (column == 6 + //&& (Project.Data.GetFlag(offset) == Data.FlagType.Opcode || Project.Data.GetFlag(offset) == Data.FlagType.Unreached) + && Project.Data.ConvertSnesToPc(Project.Data.GetIntermediateAddressOrPointer(offset)) == selOffset + ) style.BackColor = Color.DeepPink; + } + } + + private void table_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) + { + int row = e.RowIndex + ViewOffset; + if (row < 0 || row >= Project.Data.GetRomSize()) return; + PaintCell(row, e.CellStyle, e.ColumnIndex, table.CurrentCell.RowIndex + ViewOffset); + } + + public void SelectOffset(int offset, int column = -1) + { + var col = column == -1 ? table.CurrentCell.ColumnIndex : column; + if (offset < ViewOffset) + { + ViewOffset = offset; + UpdateDataGridView(); + table.CurrentCell = table.Rows[0].Cells[col]; + } + else if (offset >= ViewOffset + rowsToShow) + { + ViewOffset = offset - rowsToShow + 1; + UpdateDataGridView(); + table.CurrentCell = table.Rows[rowsToShow - 1].Cells[col]; + } + else + { + table.CurrentCell = table.Rows[offset - ViewOffset].Cells[col]; + } + } + + private void InitMainTable() + { + table.CellValueNeeded += new DataGridViewCellValueEventHandler(table_CellValueNeeded); + table.CellValuePushed += new DataGridViewCellValueEventHandler(table_CellValuePushed); + table.CellPainting += new DataGridViewCellPaintingEventHandler(table_CellPainting); + + rowsToShow = ((table.Height - table.ColumnHeadersHeight) / table.RowTemplate.Height); + + // https://stackoverflow.com/a/1506066 + typeof(DataGridView).InvokeMember( + "DoubleBuffered", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.SetProperty, + null, + table, + new object[] {true}); + } + + private void BeginEditingComment() + { + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[12]; + table.BeginEdit(true); + } + + private void BeginAddingLabel() + { + table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[0]; + table.BeginEdit(true); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.Prompts.cs b/DiztinGUIsh/window/MainWindow.Prompts.cs new file mode 100644 index 00000000..c75ccb13 --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.Prompts.cs @@ -0,0 +1,172 @@ +using System; +using System.IO; +using System.Windows.Forms; +using Diz.Core.export; +using DiztinGUIsh.window.dialog; + +namespace DiztinGUIsh.window +{ + public partial class MainWindow + { + private bool PromptContinueEvenIfUnsavedChanges() + { + if (Project == null || !Project.UnsavedChanges) + return true; + + return DialogResult.OK == MessageBox.Show( + "You have unsaved changes. They will be lost if you continue.", + "Unsaved Changes", MessageBoxButtons.OKCancel); + } + + private string PromptForOpenFilename() + { + // TODO: combine with another function here that does similar + openFileDialog.InitialDirectory = Project?.ProjectFileName ?? ""; + return openFileDialog.ShowDialog() == DialogResult.OK ? openFileDialog.FileName : ""; + } + + private static void ShowExportResults(LogCreator.OutputResult result) + { + if (result.ErrorCount > 0) + MessageBox.Show("Disassembly created with errors. See errors.txt for details.", "Warning", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + else + MessageBox.Show("Disassembly created successfully!", "Complete", MessageBoxButtons.OK, + MessageBoxIcon.Asterisk); + } + + private void PromptForFilenameToSave() + { + saveProjectFile.InitialDirectory = Project.AttachedRomFilename; + if (saveProjectFile.ShowDialog() == DialogResult.OK && saveProjectFile.FileName != "") + { + ProjectController.SaveProject(saveProjectFile.FileName); + } + } + + private bool PromptForOpenProjectFilename() + { + if (!PromptContinueEvenIfUnsavedChanges()) + return false; + + openProjectFile.InitialDirectory = Project?.ProjectFileName; + return openProjectFile.ShowDialog() == DialogResult.OK; + } + + private void viewHelpToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + System.Diagnostics.Process.Start(Directory.GetCurrentDirectory() + "/help.html"); + } + catch (Exception) + { + MessageBox.Show("Can't find the help file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void githubToolStripMenuItem_Click(object sender, EventArgs e) + { + System.Diagnostics.Process.Start("https://github.com/Dotsarecool/DiztinGUIsh"); + } + + private string PromptOpenBizhawkCDLFile() + { + openCDLDialog.InitialDirectory = Project.ProjectFileName; + if (openCDLDialog.ShowDialog() != DialogResult.OK) + return ""; + + return !PromptContinueEvenIfUnsavedChanges() ? "" : openCDLDialog.FileName; + } + + private static void ReportNumberFlagsModified(long numModifiedFlags, int numFiles = 1) + { + MessageBox.Show($"Modified total {numModifiedFlags} flags from {numFiles} files!", + "Done", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private bool PromptForImportBSNESTraceLogFile() + { + openTraceLogDialog.Multiselect = true; + return openTraceLogDialog.ShowDialog() == DialogResult.OK; + } + + private static void ShowOffsetOutOfRangeMsg() + { + ShowError("That offset is out of range.", "Error"); + } + + private int PromptForGotoOffset() + { + if (!RomDataPresent()) + return -1; + + var go = new GotoDialog(ViewOffset + table.CurrentCell.RowIndex, Project.Data); + var result = go.ShowDialog(); + if (result != DialogResult.OK) + return -1; + + return go.GetPcOffset(); + } + + private static void ShowError(string errorMsg, string caption = "Error") + { + MessageBox.Show(errorMsg, caption, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + private bool PromptHarshAutoStep(int offset, out int newOffset, out int count) + { + newOffset = count = -1; + + var harsh = new HarshAutoStep(offset, Project.Data); + if (harsh.ShowDialog() != DialogResult.OK) + return false; + + newOffset = harsh.Start; + count = harsh.Count; + return true; + } + + private MarkManyDialog PromptMarkMany(int offset, int column) + { + var mark = new MarkManyDialog(offset, column, Project.Data); + return mark.ShowDialog() == DialogResult.OK ? mark : null; + } + + private bool PromptForMisalignmentCheck() + { + if (!RomDataPresent()) + return false; + + return new MisalignmentChecker(Project.Data).ShowDialog() == DialogResult.OK; + } + + private static void ShowInfo(string s, string caption) + { + MessageBox.Show(s, caption, + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private bool PromptForInOutChecking() + { + if (!RomDataPresent()) + return false; + + return new InOutPointChecker().ShowDialog() == DialogResult.OK; + } + + public string AskToSelectNewRomFilename(string promptSubject, string promptText) + { + string initialDir = null; // TODO: Project.ProjectFileName + return GuiUtil.PromptToConfirmAction(promptSubject, promptText, + () => GuiUtil.PromptToSelectFile(initialDir) + ); + } + + public void OnProjectOpenWarning(string warningMsg) + { + MessageBox.Show(warningMsg, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.Properties.cs b/DiztinGUIsh/window/MainWindow.Properties.cs new file mode 100644 index 00000000..e1f01de0 --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.Properties.cs @@ -0,0 +1,47 @@ +using Diz.Core.model; +using Diz.Core.util; +using DiztinGUIsh.controller; + +namespace DiztinGUIsh.window +{ + public partial class MainWindow + { + public DizDocument Document { get; } = new DizDocument(); + + public Project Project + { + get => Document.Project; + set => Document.Project = value; + } + + // not sure if this will be the final place this lives. OK for now. -Dom + public ProjectController ProjectController { get; protected set; } + + IProjectView.LongRunningTaskHandler IProjectView.TaskHandler => + ProgressBarJob.RunAndWaitForCompletion; + + // sub windows + public AliasList AliasList; + private VisualizerForm visualForm; + + // TODO: add a handler so we get notified when CurrentViewOffset changes. + // then, we split most of our functions up into + // 1. things that change ViewOffset + // 2. things that react to ViewOffset changes. + // + // This will allow more flexibility and synchronizing different views (i.e. main table, graphics, layout, etc) + // and this lets us save this value with the project file itself. + + // Data offset of the "view" i.e. the top of the table + private int ViewOffset + { + get => Project?.CurrentViewOffset ?? 0; + set => Project.CurrentViewOffset = value; + } + + private bool importerMenuItemsEnabled; + + private Util.NumberBase displayBase = Util.NumberBase.Hexadecimal; + private Data.FlagType markFlag = Data.FlagType.Data8Bit; + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.ReadOnlyHelpers.cs b/DiztinGUIsh/window/MainWindow.ReadOnlyHelpers.cs new file mode 100644 index 00000000..b5cb3542 --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.ReadOnlyHelpers.cs @@ -0,0 +1,68 @@ +using Diz.Core.model; + +namespace DiztinGUIsh.window +{ + public partial class MainWindow + { + private int FindIntermediateAddress(int offset) + { + if (!RomDataPresent()) + return -1; + + var ia = Project.Data.GetIntermediateAddressOrPointer(offset); + if (ia < 0) + return -1; + + return Project.Data.ConvertSnesToPc(ia); + } + + private bool FindUnreached(int offset, bool end, bool direction, out int unreached) + { + var size = Project.Data.GetRomSize(); + unreached = end ? (direction ? 0 : size - 1) : offset; + + if (direction) + { + if (!end) + while (unreached < size - 1 && IsUnreached(unreached)) + unreached++; + + while (unreached < size - 1 && IsReached(unreached)) + unreached++; + } + else + { + if (unreached > 0) + unreached--; + + while (unreached > 0 && IsReached(unreached)) + unreached--; + } + + while (unreached > 0 && IsUnreached(unreached - 1)) + unreached--; + + return IsUnreached(unreached); + } + + private bool IsReached(int offset) + { + return Project.Data.GetFlag(offset) != Data.FlagType.Unreached; + } + + private bool IsUnreached(int offset) + { + return Project.Data.GetFlag(offset) == Data.FlagType.Unreached; + } + + private bool RomDataPresent() + { + return Project?.Data?.GetRomSize() > 0; + } + + private bool IsOffsetInRange(int offset) + { + return offset >= 0 && offset < Project.Data.GetRomSize(); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.SimpleUI.cs b/DiztinGUIsh/window/MainWindow.SimpleUI.cs new file mode 100644 index 00000000..e9aa1914 --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.SimpleUI.cs @@ -0,0 +1,137 @@ +using System; +using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; +using DiztinGUIsh.window.dialog; + +namespace DiztinGUIsh.window +{ + public partial class MainWindow + { + private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) => + e.Cancel = !PromptContinueEvenIfUnsavedChanges(); + + private void MainWindow_SizeChanged(object sender, EventArgs e) => UpdatePanels(); + private void MainWindow_ResizeEnd(object sender, EventArgs e) => UpdateDataGridView(); + private void MainWindow_Load(object sender, EventArgs e) => Init(); + private void newProjectToolStripMenuItem_Click(object sender, EventArgs e) => CreateNewProject(); + private void openProjectToolStripMenuItem_Click(object sender, EventArgs e) => OpenProject(); + + private void saveProjectToolStripMenuItem_Click(object sender, EventArgs e) => SaveProject(); + + private void saveProjectAsToolStripMenuItem_Click(object sender, EventArgs e) => PromptForFilenameToSave(); + private void exportLogToolStripMenuItem_Click(object sender, EventArgs e) => ExportAssembly(); + private void aboutToolStripMenuItem_Click(object sender, EventArgs e) => new About().ShowDialog(); + private void exitToolStripMenuItem_Click(object sender, EventArgs e) => Application.Exit(); + + private void decimalToolStripMenuItem_Click(object sender, EventArgs e) => + UpdateBase(Util.NumberBase.Decimal); + + private void hexadecimalToolStripMenuItem_Click(object sender, EventArgs e) => + UpdateBase(Util.NumberBase.Hexadecimal); + + private void binaryToolStripMenuItem_Click(object sender, EventArgs e) => + UpdateBase(Util.NumberBase.Binary); + + private void importTraceLogBinary_Click(object sender, EventArgs e) => ImportBsnesBinaryTraceLog(); + private void addLabelToolStripMenuItem_Click(object sender, EventArgs e) => BeginAddingLabel(); + private void visualMapToolStripMenuItem_Click(object sender, EventArgs e) => ShowVisualizerForm(); + private void stepOverToolStripMenuItem_Click(object sender, EventArgs e) => Step(SelectedOffset); + private void stepInToolStripMenuItem_Click(object sender, EventArgs e) => StepIn(SelectedOffset); + private void autoStepSafeToolStripMenuItem_Click(object sender, EventArgs e) => AutoStepSafe(SelectedOffset); + private void autoStepHarshToolStripMenuItem_Click(object sender, EventArgs e) => AutoStepHarsh(SelectedOffset); + private void gotoToolStripMenuItem_Click(object sender, EventArgs e) => GoTo(PromptForGotoOffset()); + + private void gotoIntermediateAddressToolStripMenuItem_Click(object sender, EventArgs e) => + GoToIntermediateAddress(SelectedOffset); + + private void gotoFirstUnreachedToolStripMenuItem_Click(object sender, EventArgs e) => + GoToUnreached(true, true); + + private void gotoNearUnreachedToolStripMenuItem_Click(object sender, EventArgs e) => + GoToUnreached(false, false); + + private void gotoNextUnreachedToolStripMenuItem_Click(object sender, EventArgs e) => + GoToUnreached(false, true); + + private void markOneToolStripMenuItem_Click(object sender, EventArgs e) => Mark(SelectedOffset); + private void markManyToolStripMenuItem_Click(object sender, EventArgs e) => MarkMany(SelectedOffset, 7); + private void setDataBankToolStripMenuItem_Click(object sender, EventArgs e) => MarkMany(SelectedOffset, 8); + private void setDirectPageToolStripMenuItem_Click(object sender, EventArgs e) => MarkMany(SelectedOffset, 9); + + private void toggleAccumulatorSizeMToolStripMenuItem_Click(object sender, EventArgs e) => MarkMany(SelectedOffset, 10); + + private void toggleIndexSizeToolStripMenuItem_Click(object sender, EventArgs e) => MarkMany(SelectedOffset, 11); + private void addCommentToolStripMenuItem_Click(object sender, EventArgs e) => BeginEditingComment(); + + private void unreachedToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Unreached); + + private void opcodeToolStripMenuItem_Click(object sender, EventArgs e) => SetMarkerLabel(Data.FlagType.Opcode); + + private void operandToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Operand); + + private void bitDataToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Data8Bit); + + private void graphicsToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Graphics); + + private void musicToolStripMenuItem_Click(object sender, EventArgs e) => SetMarkerLabel(Data.FlagType.Music); + private void emptyToolStripMenuItem_Click(object sender, EventArgs e) => SetMarkerLabel(Data.FlagType.Empty); + + private void bitDataToolStripMenuItem1_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Data16Bit); + + private void wordPointerToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Pointer16Bit); + + private void bitDataToolStripMenuItem2_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Data24Bit); + + private void longPointerToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Pointer24Bit); + + private void bitDataToolStripMenuItem3_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Data32Bit); + + private void dWordPointerToolStripMenuItem_Click(object sender, EventArgs e) => + SetMarkerLabel(Data.FlagType.Pointer32Bit); + + private void textToolStripMenuItem_Click(object sender, EventArgs e) => SetMarkerLabel(Data.FlagType.Text); + + private void fixMisalignedInstructionsToolStripMenuItem_Click(object sender, EventArgs e) => + FixMisalignedInstructions(); + + private void moveWithStepToolStripMenuItem_Click(object sender, EventArgs e) => ToggleMoveWithStep(); + private void labelListToolStripMenuItem_Click(object sender, EventArgs e) => ShowCommentList(); + + private void openLastProjectAutomaticallyToolStripMenuItem_Click(object sender, EventArgs e) => + ToggleOpenLastProjectEnabled(); + + private void closeProjectToolStripMenuItem_Click(object sender, EventArgs e) + { + // TODO + } + + private void importCDLToolStripMenuItem_Click_1(object sender, EventArgs e) => ImportBizhawkCDL(); + + private void importBsnesTracelogText_Click(object sender, EventArgs e) => ImportBsnesTraceLogText(); + + private void graphicsWindowToolStripMenuItem_Click(object sender, EventArgs e) + { + // TODO + // graphics view window + } + + private void toolStripOpenLast_Click(object sender, EventArgs e) + { + OpenLastProject(); + } + + private void rescanForInOutPointsToolStripMenuItem_Click(object sender, EventArgs e) => RescanForInOut(); + private void importUsageMapToolStripMenuItem_Click_1(object sender, EventArgs e) => ImportBSNESUsageMap(); + private void table_MouseWheel(object sender, MouseEventArgs e) => ScrollTableBy(e.Delta); + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.StateUpdate.cs b/DiztinGUIsh/window/MainWindow.StateUpdate.cs new file mode 100644 index 00000000..d0f1f5fc --- /dev/null +++ b/DiztinGUIsh/window/MainWindow.StateUpdate.cs @@ -0,0 +1,146 @@ +using System.IO; +using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; +using DiztinGUIsh.Properties; + +namespace DiztinGUIsh.window +{ + // This file is mostly about various controls reacting to state changes + // + // The direction we need to take this is being driven almost 100% by INotifyChanged events coming off + // DizDocument, Data, and Project classes --- instead of being explicitly pushed. + public partial class MainWindow + { + private void RebindProject() + { + AliasList?.RebindProject(); + if (visualForm != null) + visualForm.Project = Project; + } + + private void UpdatePanels() + { + table.Height = this.Height - 85; + table.Width = this.Width - 33; + vScrollBar1.Height = this.Height - 85; + vScrollBar1.Left = this.Width - 33; + if (WindowState == FormWindowState.Maximized) UpdateDataGridView(); + } + + public void UpdateWindowTitle() + { + Text = + (Project.UnsavedChanges ? "*" : "") + + (Project.ProjectFileName ?? "New Project") + + " - DiztinGUIsh"; + } + + private void UpdateUiFromSettings() + { + var lastOpenedFilePresent = Settings.Default.LastOpenedFile != ""; + + toolStripOpenLast.Enabled = lastOpenedFilePresent; + toolStripOpenLast.Text = "Open Last File"; + if (lastOpenedFilePresent) + toolStripOpenLast.Text += $" ({Path.GetFileNameWithoutExtension(Settings.Default.LastOpenedFile)})"; + + openLastProjectAutomaticallyToolStripMenuItem.Checked = Settings.Default.OpenLastFileAutomatically; + } + + private void RefreshUi() + { + importCDLToolStripMenuItem.Enabled = true; + UpdateWindowTitle(); + UpdateDataGridView(); + UpdatePercent(); + table.Invalidate(); + EnableSubWindows(); + } + + private void UpdateBase(Util.NumberBase noBase) + { + displayBase = noBase; + decimalToolStripMenuItem.Checked = noBase == Util.NumberBase.Decimal; + hexadecimalToolStripMenuItem.Checked = noBase == Util.NumberBase.Hexadecimal; + binaryToolStripMenuItem.Checked = noBase == Util.NumberBase.Binary; + InvalidateTable(); + } + + public void UpdatePercent() + { + if (Project?.Data == null || Project.Data.GetRomSize() <= 0) + return; + + int totalUnreached = 0, size = Project.Data.GetRomSize(); + for (int i = 0; i < size; i++) + if (Project.Data.GetFlag(i) == Data.FlagType.Unreached) + totalUnreached++; + int reached = size - totalUnreached; + percentComplete.Text = $"{reached * 100.0 / size:N3}% ({reached:D}/{size:D})"; + } + + public void UpdateMarkerLabel() + { + currentMarker.Text = $"Marker: {markFlag.ToString()}"; + } + + private void UpdateDataGridView() + { + if (Project?.Data == null || Project.Data.GetRomSize() <= 0) + return; + + rowsToShow = ((table.Height - table.ColumnHeadersHeight) / table.RowTemplate.Height); + + if (ViewOffset + rowsToShow > Project.Data.GetRomSize()) + ViewOffset = Project.Data.GetRomSize() - rowsToShow; + + if (ViewOffset < 0) + ViewOffset = 0; + + vScrollBar1.Enabled = true; + vScrollBar1.Maximum = Project.Data.GetRomSize() - rowsToShow; + vScrollBar1.Value = ViewOffset; + table.RowCount = rowsToShow; + + importerMenuItemsEnabled = true; + UpdateImporterEnabledStatus(); + } + + private void UpdateImporterEnabledStatus() + { + importUsageMapToolStripMenuItem.Enabled = importerMenuItemsEnabled; + importCDLToolStripMenuItem.Enabled = importerMenuItemsEnabled; + importTraceLogBinary.Enabled = importerMenuItemsEnabled; + importTraceLogText.Enabled = importerMenuItemsEnabled; + } + + private void EnableSubWindows() + { + labelListToolStripMenuItem.Enabled = true; + } + + public void UpdateSaveOptionStates(bool saveEnabled, bool saveAsEnabled, bool closeEnabled) + { + saveProjectToolStripMenuItem.Enabled = saveEnabled; + saveProjectAsToolStripMenuItem.Enabled = saveAsEnabled; + closeProjectToolStripMenuItem.Enabled = closeEnabled; + + exportLogToolStripMenuItem.Enabled = true; + } + + private void UpdateSomeUI2() + { + // refactor this somewhere else + UpdateUI_Tmp3(); + InvalidateTable(); + } + + private void UpdateUI_Tmp3() + { + // refactor this somewhere else + UpdatePercent(); + UpdateWindowTitle(); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.cs b/DiztinGUIsh/window/MainWindow.cs index 5fbdaa55..cec65b2c 100644 --- a/DiztinGUIsh/window/MainWindow.cs +++ b/DiztinGUIsh/window/MainWindow.cs @@ -1,858 +1,118 @@ -using DiztinGUIsh.window; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; +using System.Windows.Forms; +using Diz.Core.export; +using Diz.Core.model; +using DiztinGUIsh.controller; +using DiztinGUIsh.Properties; -namespace DiztinGUIsh +namespace DiztinGUIsh.window { - public partial class MainWindow : Form + public partial class MainWindow : Form, IProjectView { public MainWindow() { - InitializeComponent(); - } - - private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) - { - e.Cancel = !ContinueUnsavedChanges(); - } - - private void MainWindow_SizeChanged(object sender, EventArgs e) - { - table.Height = this.Height - 85; - table.Width = this.Width - 33; - vScrollBar1.Height = this.Height - 85; - vScrollBar1.Left = this.Width - 33; - if (WindowState == FormWindowState.Maximized) UpdateDataGridView(); - } - - private void MainWindow_ResizeEnd(object sender, EventArgs e) - { - UpdateDataGridView(); - } - - private void MainWindow_Load(object sender, EventArgs e) - { - table.CellValueNeeded += new DataGridViewCellValueEventHandler(table_CellValueNeeded); - table.CellValuePushed += new DataGridViewCellValueEventHandler(table_CellValuePushed); - table.CellPainting += new DataGridViewCellPaintingEventHandler(table_CellPainting); - viewOffset = 0; - rowsToShow = ((table.Height - table.ColumnHeadersHeight) / table.RowTemplate.Height); - - // https://stackoverflow.com/a/1506066 - typeof(DataGridView).InvokeMember( - "DoubleBuffered", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty, - null, - table, - new object[] { true }); - - aliasList = new AliasList(this); - } - - public void UpdateWindowTitle() - { - this.Text = - (Project.unsavedChanges ? "*" : "") + - (Project.currentFile == null ? "New Project" : Project.currentFile) + - " - DiztinGUIsh"; - } - - private bool ContinueUnsavedChanges() - { - if (Project.unsavedChanges) - { - DialogResult confirm = MessageBox.Show("You have unsaved changes. They will be lost if you continue.", "Unsaved Changes", MessageBoxButtons.OKCancel); - return confirm == DialogResult.OK; - } - return true; - } - - public void TriggerSaveOptions(bool save, bool saveas) - { - saveProjectToolStripMenuItem.Enabled = save; - saveProjectAsToolStripMenuItem.Enabled = saveas; - exportLogToolStripMenuItem.Enabled = true; - } - - private void newProjectToolStripMenuItem_Click(object sender, EventArgs e) - { - if (ContinueUnsavedChanges()) - { - openROMFile.InitialDirectory = Project.currentFile; - DialogResult result = openROMFile.ShowDialog(); - if (result == DialogResult.OK) - { - if (Project.NewProject(openROMFile.FileName)) - { - importCDLToolStripMenuItem.Enabled = true; - TriggerSaveOptions(false, true); - UpdateWindowTitle(); - UpdateDataGridView(); - UpdatePercent(); - table.Invalidate(); - EnableSubWindows(); - } - } - } - } - - private void openProjectToolStripMenuItem_Click(object sender, EventArgs e) - { - if (ContinueUnsavedChanges()) - { - openProjectFile.InitialDirectory = Project.currentFile; - DialogResult result = openProjectFile.ShowDialog(); - if (result == DialogResult.OK) - { - openProject(openProjectFile.FileName); - } - } - } - - public void openProject(string filename) - { - if (Project.TryOpenProject(filename, openROMFile)) - { - importCDLToolStripMenuItem.Enabled = true; - TriggerSaveOptions(true, true); - UpdateWindowTitle(); - UpdateDataGridView(); - UpdatePercent(); - table.Invalidate(); - EnableSubWindows(); - } - } - - private void saveProjectToolStripMenuItem_Click(object sender, EventArgs e) - { - Project.SaveProject(Project.currentFile); - UpdateWindowTitle(); - } - - private void saveProjectAsToolStripMenuItem_Click(object sender, EventArgs e) - { - saveProjectFile.InitialDirectory = Project.currentROMFile; - DialogResult result = saveProjectFile.ShowDialog(); - if (result == DialogResult.OK && saveProjectFile.FileName != "") - { - Project.SaveProject(saveProjectFile.FileName); - TriggerSaveOptions(true, true); - UpdateWindowTitle(); - } - } - - private void importCDLToolStripMenuItem_Click(object sender, EventArgs e) - { - openCDLDialog.InitialDirectory = Project.currentFile; - DialogResult result = openCDLDialog.ShowDialog(); - if (result == DialogResult.OK) - { - if (ContinueUnsavedChanges()) - { - try - { - var cdl = BizHawkCdl.LoadFromFile(openCDLDialog.FileName); - Manager.ImportBizHawkCDL(cdl); - } - catch (InvalidDataException ex) - { - MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - catch (EndOfStreamException ex) - { - MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - - UpdatePercent(); - UpdateWindowTitle(); - InvalidateTable(); - } - } - } - - private void exportLogToolStripMenuItem_Click(object sender, EventArgs e) - { - ExportDisassembly export = new ExportDisassembly(); - DialogResult result = export.ShowDialog(); - if (result == DialogResult.OK) - { - string file = null, error = null; - if (LogCreator.structure == LogCreator.FormatStructure.SingleFile) - { - saveLogSingleFile.InitialDirectory = Project.currentFile; - result = saveLogSingleFile.ShowDialog(); - if (result == DialogResult.OK && saveLogSingleFile.FileName != "") - { - file = saveLogSingleFile.FileName; - error = Path.GetDirectoryName(file) + "/error.txt"; - } - } else - { - chooseLogFolder.SelectedPath = Path.GetDirectoryName(Project.currentFile); - result = chooseLogFolder.ShowDialog(); - if (result == DialogResult.OK && chooseLogFolder.SelectedPath != "") - { - file = chooseLogFolder.SelectedPath + "/main.asm"; - error = Path.GetDirectoryName(file) + "/error.txt"; - } - } - - if (file != null) - { - int errors = 0; - using (StreamWriter sw = new StreamWriter(file)) - using (StreamWriter er = new StreamWriter(error)) - { - errors = LogCreator.CreateLog(sw, er); - if (errors > 0) MessageBox.Show("Disassembly created with errors. See errors.txt for details.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - else MessageBox.Show("Disassembly created successfully!", "Complete", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); - } - if (errors == 0) File.Delete(error); - } - } - } - - private void viewHelpToolStripMenuItem_Click(object sender, EventArgs e) - { - try - { - System.Diagnostics.Process.Start(Directory.GetCurrentDirectory() + "/help.html"); - } catch (Exception) - { - MessageBox.Show("Can't find the help file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } + ProjectController = new ProjectController { + ProjectView = this, + }; - private void githubToolStripMenuItem_Click(object sender, EventArgs e) - { - System.Diagnostics.Process.Start("https://github.com/Dotsarecool/DiztinGUIsh"); - } - - private void aboutToolStripMenuItem_Click(object sender, EventArgs e) - { - About about = new About(); - about.ShowDialog(); - } - - private void exitToolStripMenuItem_Click(object sender, EventArgs e) - { - Application.Exit(); - } + Document.PropertyChanged += Document_PropertyChanged; + ProjectController.ProjectChanged += ProjectController_ProjectChanged; - private Util.NumberBase DisplayBase = Util.NumberBase.Hexadecimal; - private Data.FlagType markFlag = Data.FlagType.Data8Bit; - private bool MoveWithStep = true; - - private void decimalToolStripMenuItem_Click(object sender, EventArgs e) - { - UpdateBase(Util.NumberBase.Decimal); - } - - private void hexadecimalToolStripMenuItem_Click(object sender, EventArgs e) - { - UpdateBase(Util.NumberBase.Hexadecimal); - } - - private void binaryToolStripMenuItem_Click(object sender, EventArgs e) - { - UpdateBase(Util.NumberBase.Binary); + InitializeComponent(); } - - private void UpdateBase(Util.NumberBase noBase) + + private void Init() { - DisplayBase = noBase; - decimalToolStripMenuItem.Checked = noBase == Util.NumberBase.Decimal; - hexadecimalToolStripMenuItem.Checked = noBase == Util.NumberBase.Hexadecimal; - binaryToolStripMenuItem.Checked = noBase == Util.NumberBase.Binary; - InvalidateTable(); - } + InitMainTable(); - public void UpdatePercent() - { - int totalUnreached = 0, size = Data.GetROMSize(); - for (int i = 0; i < size; i++) if (Data.GetFlag(i) == Data.FlagType.Unreached) totalUnreached++; - int reached = size - totalUnreached; - percentComplete.Text = string.Format("{0:N3}% ({1:D}/{2:D})", reached * 100.0 / size, reached, size); - } + AliasList = new AliasList(this); - public void UpdateMarkerLabel() - { - currentMarker.Text = string.Format("Marker: {0}", markFlag.ToString()); - } + UpdatePanels(); + UpdateUiFromSettings(); - public void InvalidateTable() - { - table.Invalidate(); + if (Settings.Default.OpenLastFileAutomatically) + OpenLastProject(); } - // DataGridView - - private int viewOffset; - private int rowsToShow; - private void UpdateDataGridView() + private void Document_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (Data.GetROMSize() > 0) + if (e.PropertyName == nameof(DizDocument.LastProjectFilename)) { - rowsToShow = ((table.Height - table.ColumnHeadersHeight) / table.RowTemplate.Height); - if (viewOffset + rowsToShow > Data.GetROMSize()) viewOffset = Data.GetROMSize() - rowsToShow; - if (viewOffset < 0) viewOffset = 0; - vScrollBar1.Enabled = true; - vScrollBar1.Maximum = Data.GetROMSize() - rowsToShow; - vScrollBar1.Value = viewOffset; - table.RowCount = rowsToShow; + UpdateUiFromSettings(); } } - private void table_MouseWheel(object sender, MouseEventArgs e) - { - if (Data.GetROMSize() <= 0) return; - int selRow = table.CurrentCell.RowIndex + viewOffset, selCol = table.CurrentCell.ColumnIndex; - int amount = e.Delta / 0x18; - viewOffset -= amount; - UpdateDataGridView(); - if (selRow < viewOffset) selRow = viewOffset; - else if (selRow >= viewOffset + rowsToShow) selRow = viewOffset + rowsToShow - 1; - table.CurrentCell = table.Rows[selRow - viewOffset].Cells[selCol]; - InvalidateTable(); - } - - private void vScrollBar1_ValueChanged(object sender, EventArgs e) - { - int selOffset = table.CurrentCell.RowIndex + viewOffset; - viewOffset = vScrollBar1.Value; - UpdateDataGridView(); - - if (selOffset < viewOffset) table.CurrentCell = table.Rows[0].Cells[table.CurrentCell.ColumnIndex]; - else if (selOffset >= viewOffset + rowsToShow) table.CurrentCell = table.Rows[rowsToShow - 1].Cells[table.CurrentCell.ColumnIndex]; - else table.CurrentCell = table.Rows[selOffset - viewOffset].Cells[table.CurrentCell.ColumnIndex]; - - InvalidateTable(); - } - - private void table_MouseDown(object sender, MouseEventArgs e) + private void ProjectController_ProjectChanged(object sender, ProjectController.ProjectChangedEventArgs e) { - InvalidateTable(); - } - - private void table_KeyDown(object sender, KeyEventArgs e) - { - if (Data.GetROMSize() <= 0) return; - - int offset = table.CurrentCell.RowIndex + viewOffset; - int newOffset = offset; - int amount = 0x01; - - Console.WriteLine(e.KeyCode); - switch (e.KeyCode) + switch (e.ChangeType) { - case Keys.Home: - case Keys.PageUp: - case Keys.Up: - amount = e.KeyCode == Keys.Up ? 0x01 : e.KeyCode == Keys.PageUp ? 0x10 : 0x100; - newOffset = offset - amount; - if (newOffset < 0) newOffset = 0; - SelectOffset(newOffset); - break; - case Keys.End: - case Keys.PageDown: - case Keys.Down: - amount = e.KeyCode == Keys.Down ? 0x01 : e.KeyCode == Keys.PageDown ? 0x10 : 0x100; - newOffset = offset + amount; - if (newOffset >= Data.GetROMSize()) newOffset = Data.GetROMSize() - 1; - SelectOffset(newOffset); - break; - case Keys.Left: - amount = table.CurrentCell.ColumnIndex; - amount = amount - 1 < 0 ? 0 : amount - 1; - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[amount]; - break; - case Keys.Right: - amount = table.CurrentCell.ColumnIndex; - amount = amount + 1 >= table.ColumnCount ? table.ColumnCount - 1 : amount + 1; - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[amount]; - break; - case Keys.S: Step(offset); break; - case Keys.I: StepIn(offset); break; - case Keys.A: AutoStepSafe(offset); break; - case Keys.T: GoToIntermediateAddress(offset); break; - case Keys.U: GoToUnreached(true, true); break; - case Keys.H: GoToUnreached(false, false); break; - case Keys.N: GoToUnreached(false, true); break; - case Keys.K: Mark(offset); break; - case Keys.L: - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[0]; - table.BeginEdit(true); + case ProjectController.ProjectChangedEventArgs.ProjectChangedType.Saved: + OnProjectSaved(); break; - case Keys.B: - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[8]; - table.BeginEdit(true); + case ProjectController.ProjectChangedEventArgs.ProjectChangedType.Opened: + OnProjectOpened(e.Filename); break; - case Keys.D: - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[9]; - table.BeginEdit(true); - break; - case Keys.M: - Data.SetMFlag(offset, !Data.GetMFlag(offset)); - break; - case Keys.X: - Data.SetXFlag(offset, !Data.GetXFlag(offset)); - break; - case Keys.C: - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[12]; - table.BeginEdit(true); - break; - } - e.Handled = true; - InvalidateTable(); - } - - private void table_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) - { - int row = e.RowIndex + viewOffset; - if (row >= Data.GetROMSize()) return; - switch (e.ColumnIndex) - { - case 0: e.Value = Data.GetLabelName(Util.ConvertPCtoSNES(row)); break; - case 1: e.Value = Util.NumberToBaseString(Util.ConvertPCtoSNES(row), Util.NumberBase.Hexadecimal, 6); break; - case 2: e.Value = (char)Data.GetROMByte(row); break; - case 3: e.Value = Util.NumberToBaseString(Data.GetROMByte(row), DisplayBase); break; - case 4: e.Value = Util.PointToString(Data.GetInOutPoint(row)); break; - case 5: - int len = Manager.GetInstructionLength(row); - if (row + len <= Data.GetROMSize()) e.Value = Util.GetInstruction(row); - else e.Value = ""; + case ProjectController.ProjectChangedEventArgs.ProjectChangedType.Imported: + OnImportedProjectSuccess(); break; - case 6: - int ia = Util.GetIntermediateAddressOrPointer(row); - if (ia >= 0) e.Value = Util.NumberToBaseString(ia, Util.NumberBase.Hexadecimal, 6); - else e.Value = ""; + case ProjectController.ProjectChangedEventArgs.ProjectChangedType.Closing: + OnProjectClosing(); break; - case 7: e.Value = Util.TypeToString(Data.GetFlag(row)); break; - case 8: e.Value = Util.NumberToBaseString(Data.GetDataBank(row), Util.NumberBase.Hexadecimal, 2); break; - case 9: e.Value = Util.NumberToBaseString(Data.GetDirectPage(row), Util.NumberBase.Hexadecimal, 4); break; - case 10: e.Value = Util.BoolToSize(Data.GetMFlag(row)); break; - case 11: e.Value = Util.BoolToSize(Data.GetXFlag(row)); break; - case 12: e.Value = Data.GetComment(Util.ConvertPCtoSNES(row)); break; } - } - private void table_CellValuePushed(object sender, DataGridViewCellValueEventArgs e) - { - string value = e.Value as string; - int result; - int row = e.RowIndex + viewOffset; - if (row >= Data.GetROMSize()) return; - switch (e.ColumnIndex) - { - case 0: Data.AddLabel(Util.ConvertPCtoSNES(row), new Data.AliasInfo() {name=value}, true); break; // todo (validate for valid label characters) - case 8: if (int.TryParse(value, NumberStyles.HexNumber, null, out result)) Data.SetDataBank(row, result); break; - case 9: if (int.TryParse(value, NumberStyles.HexNumber, null, out result)) Data.SetDirectPage(row, result); break; - case 10: Data.SetMFlag(row, (value == "8" || value == "M")); break; - case 11: Data.SetXFlag(row, (value == "8" || value == "X")); break; - case 12: Data.AddComment(Util.ConvertPCtoSNES(row), value, true); break; - } - table.InvalidateRow(e.RowIndex); + RebindProject(); } - private void table_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) + private void OnProjectClosing() { - int row = e.RowIndex + viewOffset; - if (row < 0 || row >= Data.GetROMSize()) return; - Util.PaintCell(row, e.CellStyle, e.ColumnIndex, table.CurrentCell.RowIndex + viewOffset); + UpdateSaveOptionStates(saveEnabled: false, saveAsEnabled: false, closeEnabled: false); } - public void SelectOffset(int offset, int column = -1) + public void OnProjectOpened(string filename) { - int col = column == -1 ? table.CurrentCell.ColumnIndex : column; - if (offset < viewOffset) - { - viewOffset = offset; - UpdateDataGridView(); - table.CurrentCell = table.Rows[0].Cells[col]; - } else if (offset >= viewOffset + rowsToShow) - { - viewOffset = offset - rowsToShow + 1; - UpdateDataGridView(); - table.CurrentCell = table.Rows[rowsToShow - 1].Cells[col]; - } else - { - table.CurrentCell = table.Rows[offset - viewOffset].Cells[col]; - } - } + if (visualForm != null) + visualForm.Project = Project; - private void Step(int offset) - { - SelectOffset(Manager.Step(offset, false, false, offset - 1)); - UpdatePercent(); - UpdateWindowTitle(); - } + // TODO: do this with aliaslist too. - private void StepIn(int offset) - { - SelectOffset(Manager.Step(offset, true, false, offset - 1)); - UpdatePercent(); - UpdateWindowTitle(); - } + UpdateSaveOptionStates(saveEnabled: true, saveAsEnabled: true, closeEnabled: true); + RefreshUi(); - private void AutoStepSafe(int offset) - { - int destination = Manager.AutoStep(offset, false, 0); - if (MoveWithStep) SelectOffset(destination); - UpdatePercent(); - UpdateWindowTitle(); + Document.LastProjectFilename = filename; // do this last. } - private void AutoStepHarsh(int offset) + public void OnProjectOpenFail(string errorMsg) { - HarshAutoStep harsh = new HarshAutoStep(offset); - DialogResult result = harsh.ShowDialog(); - if (result == DialogResult.OK) - { - int destination = Manager.AutoStep(harsh.GetOffset(), true, harsh.GetCount()); - if (MoveWithStep) SelectOffset(destination); - UpdatePercent(); - UpdateWindowTitle(); - } + Document.LastProjectFilename = ""; + ShowError(errorMsg, "Error opening project"); } - private void Mark(int offset) + public void OnProjectSaved() { - SelectOffset(Manager.Mark(offset, markFlag, Util.TypeStepSize(markFlag))); - UpdatePercent(); + UpdateSaveOptionStates(saveEnabled: true, saveAsEnabled: true, closeEnabled: true); UpdateWindowTitle(); } - private void MarkMany(int offset, int column) - { - MarkManyDialog mark = new MarkManyDialog(offset, column); - DialogResult result = mark.ShowDialog(); - if (result == DialogResult.OK) - { - int destination = 0; - int col = mark.GetProperty(); - switch (col) - { - case 0: - destination = Manager.Mark(mark.GetOffset(), (Data.FlagType)mark.GetValue(), mark.GetCount()); - break; - case 1: - destination = Manager.MarkDataBank(mark.GetOffset(), (int)mark.GetValue(), mark.GetCount()); - break; - case 2: - destination = Manager.MarkDirectPage(mark.GetOffset(), (int)mark.GetValue(), mark.GetCount()); - break; - case 3: - destination = Manager.MarkMFlag(mark.GetOffset(), (bool)mark.GetValue(), mark.GetCount()); - break; - case 4: - destination = Manager.MarkXFlag(mark.GetOffset(), (bool)mark.GetValue(), mark.GetCount()); - break; - case 5: - destination = Manager.MarkArchitechture(mark.GetOffset(), (Data.Architechture)mark.GetValue(), mark.GetCount()); - break; - } - if (MoveWithStep) SelectOffset(destination); - UpdatePercent(); - UpdateWindowTitle(); - InvalidateTable(); - } - } - - private void GoToIntermediateAddress(int offset) - { - int ia = Util.GetIntermediateAddressOrPointer(offset); - if (ia >= 0) - { - int pc = Util.ConvertSNEStoPC(ia); - if (pc >= 0) - { - SelectOffset(pc, 1); - } - } - } - - private void GoToUnreached(bool end, bool direction) - { - int offset = table.CurrentCell.RowIndex + viewOffset; - int size = Data.GetROMSize(); - int unreached = end ? (direction ? 0 : size - 1) : offset; - - if (direction) - { - if (!end) while (unreached < size - 1 && Data.GetFlag(unreached) == Data.FlagType.Unreached) unreached++; - while (unreached < size - 1 && Data.GetFlag(unreached) != Data.FlagType.Unreached) unreached++; - } else - { - if (unreached > 0) unreached--; - while (unreached > 0 && Data.GetFlag(unreached) != Data.FlagType.Unreached) unreached--; - } - - while (unreached > 0 && Data.GetFlag(unreached - 1) == Data.FlagType.Unreached) unreached--; - - if (Data.GetFlag(unreached) == Data.FlagType.Unreached) SelectOffset(unreached, 1); - } - - private void visualMapToolStripMenuItem_Click(object sender, EventArgs e) - { - // TODO - // visual map window - } - - private void graphicsWindowToolStripMenuItem_Click(object sender, EventArgs e) - { - // TODO - // graphics view window - } - - private void stepOverToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - Step(table.CurrentCell.RowIndex + viewOffset); - } - - private void stepInToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - StepIn(table.CurrentCell.RowIndex + viewOffset); - } - - private void autoStepSafeToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - AutoStepSafe(table.CurrentCell.RowIndex + viewOffset); - } - - private void autoStepHarshToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - AutoStepHarsh(table.CurrentCell.RowIndex + viewOffset); - } - - private void gotoToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - GotoDialog go = new GotoDialog(viewOffset + table.CurrentCell.RowIndex); - DialogResult result = go.ShowDialog(); - if (result == DialogResult.OK) - { - int offset = go.GetPcOffset(); - if (offset >= 0 && offset < Data.GetROMSize()) SelectOffset(offset); - else MessageBox.Show("That offset is out of range.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private void gotoIntermediateAddressToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - GoToIntermediateAddress(table.CurrentCell.RowIndex + viewOffset); - } - - private void gotoFirstUnreachedToolStripMenuItem_Click(object sender, EventArgs e) - { - GoToUnreached(true, true); - } - - private void gotoNearUnreachedToolStripMenuItem_Click(object sender, EventArgs e) - { - GoToUnreached(false, false); - } - - private void gotoNextUnreachedToolStripMenuItem_Click(object sender, EventArgs e) - { - GoToUnreached(false, true); - } - - private void markOneToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - Mark(table.CurrentCell.RowIndex + viewOffset); - } - - private void markManyToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - MarkMany(table.CurrentCell.RowIndex + viewOffset, 7); - } - - private void addLabelToolStripMenuItem_Click(object sender, EventArgs e) - { - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[0]; - table.BeginEdit(true); - } - - private void setDataBankToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - MarkMany(table.CurrentCell.RowIndex + viewOffset, 8); - } - - private void setDirectPageToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - MarkMany(table.CurrentCell.RowIndex + viewOffset, 9); - } - - private void toggleAccumulatorSizeMToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - MarkMany(table.CurrentCell.RowIndex + viewOffset, 10); - } - - private void toggleIndexSizeToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - MarkMany(table.CurrentCell.RowIndex + viewOffset, 11); - } - - private void addCommentToolStripMenuItem_Click(object sender, EventArgs e) - { - table.CurrentCell = table.Rows[table.CurrentCell.RowIndex].Cells[12]; - table.BeginEdit(true); - } - - private void fixMisalignedInstructionsToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - MisalignmentChecker mis = new MisalignmentChecker(); - DialogResult result = mis.ShowDialog(); - if (result == DialogResult.OK) - { - int count = Manager.FixMisalignedFlags(); - InvalidateTable(); - MessageBox.Show(string.Format("Modified {0} flags!", count), "Done!", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - } - - private void rescanForInOutPointsToolStripMenuItem_Click(object sender, EventArgs e) - { - if (Data.GetROMSize() <= 0) return; - InOutPointChecker point = new InOutPointChecker(); - DialogResult result = point.ShowDialog(); - if (result == DialogResult.OK) - { - Manager.RescanInOutPoints(); - InvalidateTable(); - MessageBox.Show("Scan complete!", "Done!", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - } - - private void unreachedToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Unreached; - UpdateMarkerLabel(); - } - - private void opcodeToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Opcode; - UpdateMarkerLabel(); - } - - private void operandToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Operand; - UpdateMarkerLabel(); - } - - private void bitDataToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Data8Bit; - UpdateMarkerLabel(); - } - - private void graphicsToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Graphics; - UpdateMarkerLabel(); - } - - private void musicToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Music; - UpdateMarkerLabel(); - } - - private void emptyToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Empty; - UpdateMarkerLabel(); - } - - private void bitDataToolStripMenuItem1_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Data16Bit; - UpdateMarkerLabel(); - } - - private void wordPointerToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Pointer16Bit; - UpdateMarkerLabel(); - } - - private void bitDataToolStripMenuItem2_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Data24Bit; - UpdateMarkerLabel(); - } - - private void longPointerToolStripMenuItem_Click(object sender, EventArgs e) + public void OnExportFinished(LogCreator.OutputResult result) { - markFlag = Data.FlagType.Pointer24Bit; - UpdateMarkerLabel(); + ShowExportResults(result); } - private void bitDataToolStripMenuItem3_Click(object sender, EventArgs e) + private LogWriterSettings? PromptForExportSettingsAndConfirmation() { - markFlag = Data.FlagType.Data32Bit; - UpdateMarkerLabel(); - } + // TODO: use the controller to update the project settings from a new one we build + // don't update directly. + // probably make our Project property be fully readonly/const/whatever [ReadOnly] attribute - private void dWordPointerToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Pointer32Bit; - UpdateMarkerLabel(); - } + var selectedSettings = ExportDisassembly.ConfirmSettingsAndAskToStart(Project); + if (!selectedSettings.HasValue) + return null; - private void textToolStripMenuItem_Click(object sender, EventArgs e) - { - markFlag = Data.FlagType.Text; - UpdateMarkerLabel(); - } + var settings = selectedSettings.Value; - private void moveWithStepToolStripMenuItem_Click(object sender, EventArgs e) - { - MoveWithStep = !MoveWithStep; - moveWithStepToolStripMenuItem.Checked = MoveWithStep; - } + ProjectController.UpdateExportSettings(selectedSettings.Value); - public OpenFileDialog GetRomOpenFileDialog() - { - return openROMFile; - } - - // sub windows - AliasList aliasList; - - private void EnableSubWindows() - { - labelListToolStripMenuItem.Enabled = true; - } - - private void labelListToolStripMenuItem_Click(object sender, EventArgs e) - { - aliasList.Show(); + return settings; } } -} +} \ No newline at end of file diff --git a/DiztinGUIsh/window/MainWindow.resx b/DiztinGUIsh/window/MainWindow.resx index 6625870e..a8509cac 100644 --- a/DiztinGUIsh/window/MainWindow.resx +++ b/DiztinGUIsh/window/MainWindow.resx @@ -162,7 +162,7 @@ 132, 17 - + 248, 17 @@ -171,11 +171,11 @@ 528, 17 - - 660, 17 + + 953, 17 - - 808, 17 + + 1096, 17 953, 17 @@ -183,3704 +183,78 @@ - AAABAAQAEBAAAAEAGABoAwAARgAAACAgAAABABgAqAwAAK4DAABAQAAAAQAYACgyAABWEAAAAAAAAAEA - GAAoIAMAfkIAACgAAAAQAAAAIAAAAAEAGAAAAAAAQAMAAAAAAAAAAAAAAAAAAAAAAAD///////////// - //////////////////////////////////////////////////////+RMBqRMBqRMBq9PyIwEAkAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////+RMBqzOyCzOyDidVx8KRYAAAAAAAAAAAAAAAAAAAD/ - //////////8AAAD///////+RMBqzOyDidVzpk4DidVwwEAkAAAAAAAAAAAAAAAD///8AAAAAAAAAAAD/ - ///////aTi7idVzpk4D44Nrpk4DidVy9PyIwEAkAAAAAAAAAAAD///8AAAAAAAD///////+RMBqzOyDi - dVzpk4DidVwwEAkAAAAAAAAAAAAAAAD///////////8AAAD///////9eHxGzOyCzOyDidVx8KRYAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////9eHxGRMBqzOyC9PyIwEAkAAAAAAAAAAAAAAAAAAAAA - AAD///8AAAAAAAD///////9eHxGRMBqzOyC9PyIwEAkAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAD/ - //////9eHxGzOyCzOyDidVx8KRYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////+RMBqzOyDi - dVzpk4DidVwwEAkAAAAAAAAAAAAAAAD///////8AAAAAAAD////////aTi7idVzpk4D44Nrpk4DidVy9 - PyIwEAkAAAAAAAD///8AAAD///8AAAD///////+RMBqzOyDidVzpk4DidVwwEAkAAAAAAAAAAAAAAAD/ - //8AAAD///8AAAD///////+RMBqzOyCzOyDidVx8KRYAAAAAAAAAAAAAAAAAAAD///////8AAAAAAAD/ - //////+RMBqRMBqRMBq9PyIwEAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////// - //////////////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAYAAAA - AACADAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////7g9Iaw5H6A1HZkyG5QxG4suGYMs - GIAqF2kjE1wfEVAbD0QWDDgSCjMRCS4QCScNByILBhsJBRYHBBMHBBEGAw4FAwwFAwkDAgMBAQIBAQIB - AQAAAP///////////////7k9IbE7IK05H6g3Hqo4Hqc3Hp40HJ40HLQ7IHUnFV8fEVMcD0YYDToUCzMR - CSwPCCUNBx8LBhoJBRQHBBEGAw8FAwwFAwoDAgcDAgIBAQIBAQAAAP///////////////70/Irg9IbQ7 - ILg9Ib0/IsA/IsJAI9lIJ+BrUJ40HH4qF2EhElMcD0YYDTgSCjEQCSkOCCILBh0KBm5qaf////////// - /////9XU1AMBAQIBAQAAAP///////////////8RBI70/Ir0/IsVBI89EJdpNLd1eQeWDbemTgNpLK686 - H4UsGF4fEU4aDj8VDDUSCi4QCSUNBx8LBhgJBcrJyevq6mxpaGpoaFhWVQMBAQIBAQAAAP////////// - /////8VBI8VBI8lCJM9EJdpPMOBsUueLdvC5rPPFuuaHctlJKaU3HnImFVcdEEcYDTgSCjAQCSkOCCAL - BhoJBUhDQv///4uJiAwFAwkDAgMBAQIBAQAAAP///////////////8xDJMxDJM5EJdpNLeFvVemTgPLA - tfvs6fvu6/bTy+R8ZcxDJIouGWEhElIbDz8VDDMRCSsOCCILBhsJBRQHBLKxsOvq6gwFAwkDAgMBAQIB - AQAAAP///////////////9NGJtFFJdVGJuN7Y+mXhe+wovbUzPvu6/vu6/vs6e6sneWCbNtSM3cnFVMc - D0QWDDgSCjAQCSQMBxsJBRYHBDAqKPX19YqJiAkDAgMBAQIBAQAAAP///////////////9hHJtVGJthH - JtpLK99lSuaFb++wovrn4/vu6/TLwuR9ZtVGJpEwGmQhElMcD0YYDTwUCzMRCSsOCDktKm5qaWxpaMrJ - yfX19SwoJwMBAQIBAQAAAP///////////////9ZGJtFFJdlJKdpLK9pPMOBqT+N3XuyklPG6ruWBatNG - JqI2HXkoFmQhElUcD0cYDToUCzMRCSsOCHNrav///////////////6WkpAIBAQIBAQAAAP////////// - /////9ZGJtlIJ9lIJ9lIJ+BrUNhHJtZGJuBsUueNec9EJac3HogtGHImFWEhElAbD0IWDDwUCzMRCSsO - CCILBhoJBRYHBBEGAwwFAwkDAgMBAQIBAQAAAP///////////////9NGJtVGJspDJMA/IslCJLg9Iac3 - HsJAI99kSJszHIUsGHUnFWgjE1gdEE4aDkIWDDgSCjMRCSkOCCILBh0KBjUsKmxpaAwFAwUCAQIBAQIB - AQAAAP///////////////9FFJcpDJMRBI7g9Ia05H540HJEwGpQxG605H4AqF3koFm8lFGQhElcdEEsZ - DkIWDDgSCjEQCSsOCCALBhoJBW5qaf///wwFAwUCAQIBAQAAAAAAAP///////////////8xDJL4/IrY8 - Iao4HpkyG4suGYUsGIYsGH4qF3koFnAlFGkjE18fEVUcD0sZDkIWDDgSCjAQCScNByALBhsJBW9qaf// - /woDAgUCAQIBAQAAAAAAAP///////////////85EJbs9Iao4HpkyG40uGYErF4ErF4gtGHooFnkoFm8l - FGgjE14fEVUcD00aDkIWDDgSCjAQCSkOCCALBh0KBm5qaf///woDAgMBAQIBAQAAAAAAAP////////// - /////9FFJb0/Iqg3HpkyG40uGYgtGIErF4ouGX4qF4AqF3QnFWQhElwfEVUcD00aDkIWDDoUCzAQCScN - ByILBhsJBW5qaf///wwFAwUCAQIBAQAAAAAAAP///////////////9NGJsA/IrM7IKU3Hpw0HJYyG5Ew - GpQxG540HIgtGHcnFW0kFGEhElgdEE0aDkQWDDoUCzMRCSkOCCQMBx0KBm1qaf///wwFAwkDAgIBAQAA - AAAAAP///////////////9pLK8xDJLs9Ia05H6c3HqU3HqU3Hr0/ItxYOpszHIYsGHQnFWYhElgdEE0a - DkIWDDoUCzMRCSsOCCQMBxoJBW1qaf///wwFAwkDAgIBAQIBAQAAAP///////////////9tRMtZGJsdC - JL0/Ir0/IsA/Is9EJeBoTeaFb8pDJKg3HogtGGsjE1oeEFAbD0QWDDoUCzMRCSsOCCALBhgJBVxYV9XU - 1A4FAwkDAgMBAQIBAQAAAP///////////////9tRMtpLK9ZGJtNGJtZGJttSM+BsUuugj++ypOJ1XM5E - JaU3HnUnFV4fEVMcD0QWDDoUCzEQCSUNBx8LBhYHBBEGAw8FAwwFAwkDAgMBAQIBAQAAAP////////// - /////9tUNdpOLtlIJ9pOLt9mS+WCbO6snfjg2vvs6fLBtuFuVMA/IoMsGGMhElUcD0IWDDYSCjAQCSUN - B3FqaaelpKelpJmXl0VCQQkDAgMBAQIBAQAAAP///////////////9tRMtpOLtpOLuR/aOqZh++zpfbU - zPvu6/vu6/vs6e2omOR8ZdlJKXUnFVIbDz8VDDMRCSwPCCQMB6ilpfX19dXU1Ovq6vX19VhWVQMBAQIB - AQAAAP///////////////9pOLtpPMNxVN+BqT+aHceymlvTLwvvu6/vu6/fZ0ueLdtpOLpcyG18fEU0a - Dj0UCzMRCSsOCCQMB6ilpdXV1BEGAw4FA8rJyeDf3wMBAQIBAQAAAP///////////////9pLK9pOLt1e - Qd9mS+J1XOiSfuyikfPFuvXPxumVgtpLK6U3HnAlFFgdEEcYDToUCzAQCSsOCCALBqilpdXV1BEGAw4F - A2poaP///wIBAQIBAQAAAP///////////////9lJKdtUNd9kSOBrUOmVguN3XuN5YeiRfeyjk9xVN686 - H4MsGGMhElMcD0EWDDYSCi4QCSUNBx8LBqilpdXV1BEGAw8FA2poaP///wMBAQIBAQAAAP////////// - /////9pLK9xXOd1bPt5gRON3Xt9kSNtSM91eQeN7Y6w5H4YsGGsjE1cdEEcYDToUCzEQCSkOCCILBh0K - BqelpNXV1BEGAwwFA8nJycnJyAMBAQIBAQAAAP///////////////9lIJ9pPMNxVN9tSM9tUNdNGJr4/ - IrE7IMlCJIAqF2gjE1cdEEkYDToUCzMRCSwPCCUNBx8LBhoJBaelpPX19dXU1Ovq6vX19VZVVQIBAQAA - AAAAAP///////////////9NGJs5EJdNGJsxDJL0/Iqg3HpQxG4MsGHQnFWMhElMcD0kYDTwUCzMRCS4Q - CScNByALBhsJBRYHBGxpaKalpKalpKWkpENBQAMBAQIBAQIBAQAAAP///////////////85EJb4/IrQ7 - IKU3HpkyG4UsGHcnFWYhElgdEFIbD0IWDDwUCzMRCSwPCCUNByILBhsJBRgJBRQHBBEGAw8FAwwFAwoD - AgMBAQMBAQIBAQAAAAAAAP////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAEAAAACAAAAAAQAYAAAA - AAAAMgAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////7g9IbQ7IK05H6c3HqA1HZkyG5IwGo8wGoouGYUsGIAqF3koFnUnFXImFWsjE3AlFF4fEVgd - EFMcD00aDkYYDUIWDD0UCzgSCjUSCjMRCTAQCS4QCSsOCCkOCCcNByQMByALBh0KBhsJBRgJBRYHBBMH - BBEGAxEGAxEGAw8FAw4FAwwFAwwFAwoDAgkDAgkDAgcDAgIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AP///////////////////////7s9IbY8IbE7IKo4HqM2HZw0HJszHJYyG5IwGo0uGYouGYUsGIErF3oo - FnkoFnkoFmkjE2MhEloeEFcdEFAbD0kYDUYYDT8VDDwUCzUSCjMRCTEQCTAQCSwPCCkOCCUNByILBiAL - Bh0KBhoJBRgJBRQHBBMHBBEGAxEGAw8FAw8FAw4FAwwFAwoDAgoDAgkDAgcDAgMBAQIBAQIBAQIBAQAA - AAAAAAAAAAAAAAAAAP///////////////////////70/Irg9IbM7IK05H6g3HqM2HaA1HZszHJszHJky - G5YyG5QxG48wGoouGYUsGIUsGHcnFXAlFGgjE18fEVgdEFMcD0sZDkkYDUIWDDoUCzUSCjMRCTEQCS4Q - CSsOCCcNByUNByILBh8LBhsJBRoJBRYHBBMHBBMHBBEGAxEGAw8FAw4FAwwFAwwFAwoDAgkDAgkDAgUC - AQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////70/Irk9IbQ7IK86H6w5H6g3 - Hqg3HqM2HaI2HaM2HZ40HJ40HJw0HJcyG5YyG5QxG7Y8IZYyG3QnFWsjE2MhEloeEFMcD1AbD0cYDUIW - DDoUCzgSCjMRCTEQCS4QCSsOCCcNByQMByALBh8LBhsJBRgJBRQHBBMHBBEGAxEGAw8FAw4FAw4FAwwF - AwoDAgkDAgkDAgcDAgIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP///////////////////////70/Irg9 - Ibg9IbM7IK86H686H605H6w5H6o4Hq05H6o4Hqw5H6g3Hqc3HqA1Hag3Ht1eQc5EJYouGXwpFm8lFGMh - El4fEVUcD04aDkcYDUIWDDwUCzUSCjMRCTAQCSwPCCsOCCUNByQMBx8LBhsJBRoJBRYHBBQHBBEGAxEG - AxEGAw8FAw4FAwwFAwoDAgoDAgkDAgcDAgIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////70/Irs9Ibg9IbY8IbQ7IK86H7E7ILQ7ILM7ILQ7ILQ7ILg9Ibs9Ibg9IbY8IcdCJOJyWd1b - Pqo4Ho0uGYAqF3QnFWgjE1oeEFUcD04aDkYYDUEWDDwUCzUSCjMRCTAQCSsOCCcNByQMByILBh8LBhsJ - BRgJBW5qaf///////////////////////////////////////wMBAQIBAQIBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////70/Ir0/Irk9Ibk9Ibg9Ibg9Ibk9Ibk9Ib0/Ir4/IsJAI8VBI8dCJMxD - JM9EJdxXOeaHceN3XtNGJq05H5cyG4YsGHcnFWYhElwfEVUcD00aDkcYDT8VDDgSCjUSCjAQCSwPCCkO - CCUNByILBh8LBh0KBhoJBRYHBNXV1P///////////////////////////////////wUCAQMBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////74/IsA/Irs9Ib0/Irs9Ibs9Ib0/IsA/IsJAI8lC - JM5EJdNGJtlJKdtSM91aPON3XumXheaHct5gQ8xDJLM7IKI2HYsuGXkoFmYhElcdEFIbD0sZDkQWDDwU - CzUSCjMRCTAQCSsOCCcNByQMByILBh0KBhoJBRYHBFxYV////////////769vaalpKalpKWkpKWkpKWk - pAUCAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP///////////////////////8VBI8RBI8A/Ir0/Ir0/IsA/ - IsJAI8VBI8xDJNVGJtlJKdtUNd1eQd9lSuJ1XOeNee2omOuejON5YdxVN8pDJLY8IaA1HYsuGXImFV8f - EVcdEE4aDkYYDUEWDDoUCzUSCjAQCS4QCSkOCCUNByILBh8LBhsJBRgJBRQHBL69vf///////769vQ4F - AwwFAwoDAgoDAgkDAgcDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////8RBI8JA - I8RBI8RBI8RBI8RBI8dCJMxDJNZGJtpLK9tUNd9kSOBsUuR8ZeiRfe2omfG9sfC2qeqciuJ1XNxXOc9E - JcJAI540HIYsGGkjE1wfEVIbD0sZDkIWDDwUCzYSCjMRCS4QCSsOCCcNByILBh8LBh0KBhoJBRYHBEhD - Qv///////////0ZCQQwFAwwFAwoDAgkDAgcDAgMBAQMBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////8dCJMVBI8RBI8RBI8VBI8lCJM5EJdFFJdlIJ9tSM95gROJ0W+aFb+qZh+6un/PFu/bTy/XR - yfG8sOqZh+JyWdpPMMVBI6o4HpYyG3cnFWQhElcdEFIbD0kYDT8VDDgSCjMRCTAQCSwPCCkOCCUNByAL - BhsJBRoJBRYHBBMHBL69vf///////9XU1AwFAwwFAwoDAgkDAgcDAgMBAQIBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////8xDJMpDJMlCJMlCJMxDJM5EJc9EJdZGJttSM99kSOJyWeaHcemXhe+z - pfXNxPnh3Pvs6fvs6fjg2vLAteiRfeBrUNlJKbs9IaI2HX4qF2sjE1wfEVMcD04aDkQWDD0UCzUSCjEQ - CS4QCSkOCCUNByILBh0KBhoJBRYHBBQHBEdDQfX19f///////2tpaAwFAwoDAgkDAgcDAgMBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////85EJcxDJMpDJMpDJMxDJM5EJdhHJttSM99kSON7 - Y+aJdOufju+zpfTKwfni3fvu6/vu6/vu6/vu6/jg2u+zpeeLduBoTdpLK7s9IZIwGnkoFmQhEloeEFUc - D0kYDT8VDDoUCzMRCTAQCSsOCCUNByILBh8LBhsJBRgJBRQHBBEGA6alpP///////9XU1AoDAgoDAgkD - AgcDAgMBAQMBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9FFJc9EJc5EJcxDJNFFJdNG - Jt5gROWCbOiPe+qciuyklO+ypPLBtvbUzPvs6Pvu6/vu6/vu6/vu6/vs6fTKwe6snemTgOWBauBrUNpO - LrM7IHQnFV4fEVMcD0cYDUIWDD0UCzUSCjMRCS4QCScNByILBh8LBhsJBRgJBRYHBBEGAzAqKPX19f// - /////2tpaAoDAgkDAgkDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9VGJtNG - JtNGJtFFJdNGJtZGJttUNeFwV+WDbeiSfuufju6snfG8sPXRyfro5Pvu6/vu6/vu6/vu6/vs6PPHve2q - m+mVguWCbOFvVdxXOcVBI3koFlcdEFIbD0kYDUIWDD0UCzgSCjMRCTAQCSsOCCUNBx8LBhsJBRoJBRYH - BBMHBBEGA6alpP///////9XU1AoDAgkDAgcDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////9ZGJtZGJtZGJtVGJtFFJdVGJthHJtpLK9xVN+BrUON5YeiRfeyklPK/s/jb1fvu6/vu6/vu - 6/vu6/jb1e+ypOiRfeFvVdxVN8dCJJszHHkoFmMhElcdEFIbD0kYDUQWDD8VDDgSCjMRCTEQCS4QCSsO - CCQMBx0KBhgJBRQHBBMHBBEGAzAqKPX19f///////2poaAkDAgcDAgMBAQIBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////9lIJ9hHJtZGJtNGJtNGJtlIJ9hHJthHJtpNLd1aPN9mS+J0W+aHcuuf - jvLBtvfZ0vni3frm4ffX0PC2qeeMd+BoTdlIJ70/Ip40HIMsGHAlFGMhEloeEFIbD00aDkYYDT0UCzoU - CzUSCjEQCSwPCCkOCCQMByALBtbV1NbV1NXV1NXU1NXU1PX19f///////+rq6gkDAgcDAgMBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////9lIJ9VGJtVGJtNGJtFFJdVGJtlIJ9lJKdpNLdpP - MN5gQ+BsUuFwV+WCbOmXhe+0p/PFu/PFuu+wouiPe+BoTdpNLb4/Iqc3Ho8wGnkoFm8lFGYhEloeEFMc - D0sZDkYYDT8VDDwUCzUSCjMRCTAQCSsOCCUNByALBv///////////////////////////////////4qI - iAUCAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9lIJ9ZGJtVGJs9EJdhHJtpN - LdlJKdpLK9pOLtpOLttSM95iRuBoTd9mS+N3XumTgO2qm+6sneeNeeBrUNpLK8RBI605H5szHIouGXUn - FWkjE2EhElcdEFMcD00aDkcYDT8VDDgSCjUSCjEQCS4QCSsOCCcNByQMB9bV1NbV1NXV1NXU1NXU1NXU - 1NXU1NXU1NXU1L29vQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9lIJ9ZG - JtNGJtZGJtlJKdlIJ9hHJtlJKdlIJ91eQd1bPtpNLdtSM9pOLt1aPON5YemWg+mVguFwV9pNLcRBI686 - H5szHI0uGYAqF3QnFWkjE18fEVcdEFAbD0sZDkIWDD8VDD0UCzYSCjEQCS4QCSkOCCUNByILBh8LBhoJ - BRgJBRQHBBEGAxEGAw8FAwwFAwoDAgkDAgMBAQMBAQIBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////9hHJtVGJtFFJdlJKdlIJ9hHJtNGJtVGJtxXOeN5YdxYOsxDJMlCJMlCJM9EJd1eQeaFb+WD - bdxVN74/Iqw5H540HI0uGYErF3cnFW0kFGYhEl8fEVUcD04aDkcYDUEWDD0UCzgSCjUSCjMRCTAQCSsO - CCcNByALBh8LBhoJBRgJBRYHBBEGAw8FAw4FAwwFAwwFAwkDAgUCAQMBAQIBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////9VGJtVGJs9EJdhHJtVGJs5EJclCJMVBI89EJdVGJsA/Ir0/Iq86H686 - H7E7INFFJeJyWeFuVMxDJKM2HZQxG4ouGYAqF3koFnAlFGgjE18fEVoeEFMcD04aDkcYDUEWDDwUCzYS - CjMRCTEQCSwPCCkOCCUNByILBh8LBhsJBRgJBRQHBBMHBA8FAwwFAwoDAgkDAgUCAQMBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////9FFJdFFJcpDJM9EJc9EJcVBI74/Irs9Ib0/Ir0/ - IrY8Ia86H6A1HZw0HJ40HLM7IN9kSNxXOa05H40uGYUsGHwpFnkoFnImFWkjE2YhEl8fEVcdEFAbD0kY - DUcYDUEWDDwUCzYSCjMRCTEQCSsOCCcNByQMByALBh8LBhsJBRoJBW9qaaelpKalpEVCQQwFAwkDAgMB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////9NGJtFFJcdCJM5EJc5EJcRB - I70/Irk9IbY8IbM7IKo4HqI2HZYyG5QxG5QxG5szHNlJKcdCJJszHIMsGHwpFnkoFncnFW8lFGkjE2Mh - El8fEVcdEFIbD0sZDkYYDUIWDD0UCzYSCjMRCTEQCSwPCCkOCCUNByALBh0KBhoJBRYHBKelpP////// - /2xpaAoDAgkDAgMBAQMBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP///////////////////////9VGJsxD - JMlCJMVBI8dCJMA/Irs9IbQ7ILM7IKc3HqU3HpcyG48wGosuGYouGYsuGZkyG5EwGpEwGnooFnkoFnUn - FXAlFG8lFGgjE2MhElgdEFUcD04aDkkYDUYYDUEWDD0UCzYSCjMRCTAQCSsOCCkOCCQMByALBh0KBhoJ - BRgJBaelpP///////2xpaAoDAgUCAQUCAQMBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP////////////// - /////////9NGJspDJMdCJL4/Ir0/Ir0/IrY8Ia86H6c3HqA1HZcyG40uGYgtGIYsGIMsGIYsGHooFn4q - F4ouGXkoFnUnFXImFW8lFGsjE2YhEl8fEVoeEFMcD1AbD0sZDkYYDUEWDDwUCzYSCjMRCTEQCSsOCCUN - ByQMByALBh0KBhoJBRgJBaelpP///////2xpaAoDAgUCAQUCAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AP///////////////////////9NGJsxDJMA/Ir0/IrM7IKo4Hqc3HqA1HZszHI0uGYgtGIUsGH4qF4Aq - F3wpFoUsGHkoFnkoFoMsGHkoFnQnFW0kFGkjE2YhEmQhElwfEVcdEFMcD04aDksZDkYYDUEWDD0UCzYS - CjMRCS4QCSsOCCcNByQMByALBh0KBhsJBRoJBaelpP///////2xpaAoDAgUCAQMBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAP///////////////////////9NGJsxDJMJAI7k9IbM7IKw5H6A1HZkyG5QxG40u - GYYsGIAqF34qF4AqF3ooFoYsGHkoFnkoFnwpFnkoFnQnFW0kFGkjE2gjE2EhElwfEVcdEFUcD1AbD00a - DkcYDUEWDD0UCzgSCjMRCS4QCSsOCCcNByUNByALBh0KBhsJBRgJBaelpP///////2xpaAoDAgcDAgMB - AQMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////9ZGJs5EJcVBI70/Iq86H6c3 - HqA1HZcyG48wGogtGIUsGIAqF34qF34qF3ooFogtGHkoFnooFncnFXkoFnAlFG8lFGgjE2QhEl8fEV4f - EVcdEFMcD1AbD0sZDkcYDUEWDDwUCzgSCjMRCTAQCSsOCCcNByILBiALBh0KBh0KBhgJBaelpP////// - /2xpaAoDAgkDAgMBAQMBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////9VGJs9E - JcRBI70/Iq86H6g3Hp40HJYyG5IwGoouGYYsGIMsGIErF4AqF34qF4gtGHkoFnooFncnFXwpFnQnFXAl - FGsjE2MhEl8fEVwfEVcdEFUcD1AbD00aDkYYDUEWDDwUCzgSCjMRCTEQCSsOCCkOCCQMByALBh0KBhoJ - BRgJBaelpP///////2tpaAwFAwoDAgUCAQMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////// - /////////9VGJs5EJcRBI70/IrM7IKc3Hp40HJszHJQxG40uGYsuGYouGYYsGIErF4MsGIouGYAqF34q - F3koFn4qF3UnFXImFWkjE2QhEmEhEloeEFgdEFMcD1AbD0sZDkcYDUEWDDwUCzgSCjMRCS4QCSsOCCUN - ByQMByALBh0KBhsJBRgJBaelpP///////2tpaAoDAgoDAgUCAQMBAQIBAQIBAQAAAAAAAAAAAAAAAAAA - AP///////////////////////9lJKdFFJclCJL4/IrY8Ia05H6U3HqM2HZw0HJkyG5QxG5IwGpEwGo0u - GYsuGY8wGpYyG4YsGH4qF4UsGHkoFnUnFW0kFGsjE2QhEmEhElgdEFcdEFAbD0sZDkcYDUIWDD0UCzgS - CjUSCjEQCS4QCSkOCCQMByILBh0KBhsJBRoJBaelpP///////2xpaAwFAwoDAgkDAgIBAQIBAQIBAQAA - AAAAAAAAAAAAAAAAAP///////////////////////9lJKdVGJsxDJL4/Irk9IbQ7IK05H6U3HqM2HZ40 - HJszHJkyG5YyG5QxG5IwGpYyG9hHJrQ7IIouGYgtGHooFncnFXQnFWsjE2YhEmEhEl4fEVcdEFAbD00a - DkYYDUIWDD0UCzgSCjUSCjEQCSwPCCkOCCUNByILBh0KBhsJBRgJBaalpP///////2xpaAwFAwoDAgcD - AgIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP///////////////////////9pNLdlJKc9EJclCJL4/Irg9 - Ia86H6g3Hqc3HqM2HZw0HKA1HZ40HJw0HJw0HKg3Ht9mS9pLK5szHJYyG4YsGH4qF3koFnImFWkjE2Qh - El4fEVcdEFIbD00aDkcYDUIWDD0UCzgSCjMRCTEQCSwPCCkOCCUNByQMBx0KBhsJBRQHBKelpP////// - /2tpaAwFAwoDAgcDAgMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9pPMNlJ - KdVGJsxDJMVBI70/Irg9IbE7IKo4Hqc3Hqc3HqU3Hqg3Hqg3Hq86H8pDJON3Xt9kSLg9IaA1HZQxG4su - GYAqF3QnFWsjE2QhElwfEVgdEFMcD0sZDkcYDUEWDD0UCzgSCjUSCjEQCS4QCSsOCCUNByILBh0KBhYH - BBQHBKalpP///////2xpaAwFAwoDAgkDAgUCAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////9tRMtpOLthHJtNGJspDJMRBI70/IrY8IbY8IbY8IbQ7ILY8Ibk9IcA/IsdCJNxXOeaHcuN7 - Y9pNLb0/Iqw5H5w0HIsuGX4qF3QnFWkjE2EhEloeEFUcD00aDkkYDUIWDD0UCzoUCzUSCjEQCS4QCSkO - CCUNByALBhsJBRgJBRQHBKelpP///////2xpaAwFAwkDAgkDAgUCAQIBAQIBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////9xVN9tRMtlJKdZGJs9EJcdCJMRBI8JAI8A/Ir4/IsJAI8lCJM5EJdlI - J9tUNeJ1XOqZh+eMd99mS9ZGJsA/IrE7IKA1HY0uGXooFmsjE2EhEloeEFUcD1AbD0YYDUIWDD8VDDgS - CjUSCjEQCS4QCSsOCCILBh8LBhoJBRYHBBQHBEhDQmxpaGxpaC8qKAwFAwoDAgkDAgcDAgMBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////9tUNdpPMNpOLtpLK9hHJtFFJcxDJMpDJMxDJM5E - JdNGJtlIJ9tSM91eQeFwV+iPe+2qm+yklOaFb99kSNhHJsJAI7M7IJ40HIUsGHImFWYhElwfEVMcD1Ab - D0kYDUIWDD0UCzgSCjMRCTEQCSwPCCUNByILBh0KBhsJBRYHBBQHBBEGAxEGAw8FAw4FAwoDAgoDAgkD - AgcDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9xVN9tRMtpNLdlIJ9hHJtlI - J9hHJtZGJtVGJtlIJ9tRMt1eQeBqT+N7Y+iRfe6un/LDuPK/s+ymluaFb95gRNhHJsVBI6c3HpEwGnko - FmgjE1wfEVcdEFMcD0kYDUIWDD0UCzoUCzMRCTAQCSkOCCUNByALBh8LBhoJBRYHBBQHBBEGAxEGAw8F - AwwFAwwFAwoDAgkDAgcDAgMBAQMBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9xXOdtU - NdpPMNpOLtpLK9hHJthHJtpLK9tRMt1cP99kSOJyWeaFb+qaiPC2qfXPx/jg2vje2PTLwu2qm+WBat1c - P85EJbQ7IJw0HHwpFm0kFF8fEVcdEFIbD0sZDkQWDD0UCzYSCjEQCS4QCSkOCCUNByALBh0KBhoJBRYH - BBMHBBEGAxEGAw4FAwwFAwwFAwoDAgkDAgcDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////9xVN9tSM9pOLtpNLdpLK9lJKdpNLdpPMN1bPuBrUON7Y+eMd+yklPG6rvbVzvvs6Pvu6/vu - 6/vu6/bTy+yklOR8ZdxYOspDJKw5H4MsGHImFWQhElwfEVcdEEsZDj8VDDgSCjMRCTEQCS4QCSkOCCQM - ByALBnFqaf////////////////////X19crJyWtpaAoDAgkDAgcDAgMBAQMBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////9xVN9tRMtpPMNpLK9pOLtpNLdxVN+BrUON7Y+eLduqaiO2qm/G9sfXP - x/ro5Pvu6/vu6/vu6/vu6/vs6PLDuOyikeWDbeBsUtxVN70/IpEwGmsjE1gdEFUcD0cYDT8VDDgSCjMR - CTEQCSwPCCkOCCQMByALBnFqaf///////////////////////////////7GwsAkDAgcDAgMBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////9tSM9tRMtpOLtpOLtpOLttSM+R9ZumTgOqciuyk - lO6un/C5rPPFuvfZ0vvq5vvu6/vu6/vu6/vu6/vs6fTKwe6uoOmXheaHcuN3Xt5gRNZGJnwpFlcdEE0a - DkQWDD0UCzYSCjMRCS4QCSsOCCcNByILBh8LBnBqaf///////9XU1KalpKalpMrJyf///////////3t5 - eAcDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9tRMtpOLtpOLtpOLtpOLttS - M91bPuFuVOWBauiPe+ugj++wovLAtfbTy/rn4/vu6/vu6/vu6/vu6/nk3/G9seuejOR/aN9mS9pLK7E7 - IIAqF18fEVIbD00aDkIWDD0UCzUSCjMRCS4QCSsOCCUNByQMBx8LBnBqaf///////6alpBEGAw8FAw4F - A4qJiP///////+rq6gcDAgMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9pOLtpO - LtpOLtpOLtpOLtxVN91aPN5gQ+BqT+N5YeaJdOqaiO2qm/LAtfjb1fvs6Pvu6/vu6/ro5PPHveuejOJ1 - XNtSM8VBI6M2HXooFmkjE1wfEVMcD0kYDUIWDDoUCzUSCjAQCS4QCSkOCCUNByILBh0KBm9qaf////// - /6alpBEGAw8FAw4FAwwFA8rJyf///////2poaAMBAQIBAQIBAQAAAAAAAAAAAAAAAP////////////// - /////////9pOLtpLK9pNLdpOLttRMtxXOd1bPt5gQ+BqT+JyWeaHceiPe+qZh+2omfLAtfbTy/jb1ffa - 0/PFu+yklON4YNxVN8dCJKo4Ho0uGXQnFWgjE1oeEE4aDkYYDT8VDDoUCzYSCjEQCS4QCSsOCCQMByAL - Bh0KBm9qaf///////6alpBEGAw8FAw4FAwwFA4qIiP///////5eWlgIBAQIBAQIBAQAAAAAAAAAAAAAA - AP///////////////////////9pNLdpLK9pLK9pNLdtUNd9kSN9mS+BsUuJyWeN4YOWBauiPe+qaiOmX - heyklPC2qfLDuPLDuOymluWDbd1bPs9EJbQ7IJszHIErF20kFF8fEVcdEEsZDkcYDT8VDDgSCjMRCS4Q - CSsOCCcNByQMByALBh0KBm9qaf///////6alpBEGAw8FAw4FAwwFA2poaP///////6SkpAIBAQIBAQIB - AQAAAAAAAAAAAAAAAP///////////////////////9lJKdlJKdpNLdpPMN1cP95iRuBoTeBrUOFvVeR9 - ZuWDbeR/aOaHcuWCbOaHcuqciu6un+2qm+aJdN5iRtFFJbg9IZ40HIsuGXcnFWYhElgdEFMcD0cYDUEW - DDoUCzgSCjMRCTAQCSsOCCQMByILBh0KBhsJBW9qaf///////6alpBEGAxEGAw8FAwwFA2poaP////// - /6SkpAMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9lJKdlJKdpOLtxXOd1aPN9k - SN9mS+BrUOR9Zu2omeeNeeFuVOFwV+FvVeFwV+aFb+uejOmXheBsUtlIJ7s9IaM2HY0uGXkoFm0kFF4f - EVcdEE4aDkIWDD0UCzYSCjMRCS4QCSsOCCcNBycNByQMBx8LBhsJBW5qaf///////6alpBEGAw8FAw4F - Aw4FA5iXlv///////4mIhwMBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9lIJ9pL - K9pNLdxXOd1eQd1eQd1cP99kSON4YOWCbOFuVOBqT91cP91bPtxVN+BrUOeMd+aHcdxXObs9IaA1HY8w - GnooFnAlFGMhElcdEFAbD0kYDUIWDDoUCzUSCjEQCSwPCCkOCCUNByILBh8LBh0KBhsJBW9qaf////// - /6alpA8FAw8FAwwFAwwFA9XU1P///////0FAQAMBAQMBAQMBAQAAAAAAAAAAAAAAAP////////////// - /////////9lIJ9pLK9pLK9xVN91bPt1aPN1aPN1bPt5iRuBoTd5gQ91cP9pOLtlIJ9NGJtpOLuR8ZeFu - VMlCJJszHIouGXwpFnAlFGQhElgdEFMcD00aDkIWDDwUCzgSCjMRCS4QCSsOCCcNByQMByILBh8LBhsJ - BRgJBW1qaf///////6alpBEGAw4FAwwFA769vf///////9TU1AMBAQIBAQIBAQAAAAAAAAAAAAAAAAAA - AP///////////////////////9lIJ9lIJ9lIJ9tUNd1aPNxVN9xVN9xVN91aPN1bPttRMtpNLc5EJcdC - JL0/IsA/It9mS9tSM6o4HoouGXkoFm0kFGMhEloeEFIbD00aDkYYDTwUCzgSCjMRCTEQCS4QCSkOCCUN - ByILBiALBhsJBRoJBRYHBG1qaf///////+vq6tXU1NXU1PX19f////////X19VZVVQMBAQIBAQIBAQAA - AAAAAAAAAAAAAAAAAP///////////////////////9hHJthHJthHJtpNLdtRMttSM9pPMNpOLtpPMNpL - K9ZGJsdCJLs9IbM7IKc3Hp40HMJAI6g3HpIwGnUnFWsjE2EhElgdEFIbD0sZDkQWDD0UCzgSCjMRCTEQ - CSwPCCkOCCcNByQMByALBh0KBhoJBRgJBRYHBG1qaf////////////////////////////X19VZVVQMB - AQMBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP///////////////////////9hHJtNGJtZGJtFFJdhHJtlJ - KdlIJ9ZGJs9EJcpDJL0/IrE7IKo4HpszHJYyG4ouGYErF3koFoAqF2gjE14fEVcdEFAbD00aDkcYDT8V - DDgSCjUSCjAQCTAQCSsOCCcNByQMByILBh8LBh0KBhgJBRYHBBQHBFtXVtXU1NXU1NXU1NXU1NXU1MnJ - yZiXlisoJwMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAP///////////////////////9ZGJtFF - Jc5EJcpDJMdCJMVBI8VBI8JAI7s9Ia05H6c3Hpw0HJIwGoouGYErF3ooFnImFW0kFHAlFF4fEVcdEE4a - DkcYDUQWDD8VDDoUCzMRCTAQCS4QCSsOCCcNByUNByILBh8LBhsJBRoJBRgJBRQHBBMHBBEGAxEGAw8F - Aw4FAwwFAwwFAwoDAgkDAgUCAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAP////////////// - /////////9NGJs5EJcdCJMA/IsA/Irs9IbM7IKw5H6c3HqI2HZcyG4suGYErF3wpFnImFWsjE2QhElwf - EV4fEVUcD00aDkYYDUEWDD0UCzoUCzMRCTAQCS4QCSsOCCcNByUNByILBiALBh0KBhsJBRgJBRYHBBQH - BBMHBBEGAxEGAw8FAw4FAwwFAwoDAgoDAgcDAgMBAQMBAQMBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AP///////////////////////85EJcxDJMRBI70/IrQ7IKw5H6U3Hpw0HJcyG48wGoMsGHwpFnUnFW0k - FGQhEl4fEVgdEFMcD1AbD04aDkQWDD8VDDoUCzYSCjEQCTEQCSwPCCsOCCcNByQMByILBiALBh0KBhoJ - BRgJBRYHBBQHBBMHBBEGAxEGAw8FAw8FAw4FAwwFAwoDAgoDAgUCAQMBAQMBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAP///////////////////////8dCJMJAI70/IrQ7IKw5H6I2HZkyG5EwGoYsGH4q - F3cnFW8lFGgjE18fEVgdEFIbD04aDkkYDUQWDEYYDToUCzgSCjMRCTEQCSwPCCsOCCcNByUNByQMByIL - Bh8LBh0KBhsJBRgJBRgJBRQHBBMHBBEGAxEGAw8FAw4FAwwFAwwFAwoDAgoDAgUCAQMBAQMBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAABAAAAAgAAAQAYAAAA - AAAAIAMAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////7Y8Ibg9Ibg9IbE7ILY8Iao4Hq05H7E7IK05H6o4Hqg3Hqg3Hqg3HqA1HaM2 - HaI2HZ40HKM2HZszHJkyG5kyG5EwGpYyG5YyG5EwGpQxG40uGZEwGpEwGo8wGosuGYouGYsuGYYsGIYs - GIgtGIouGYMsGIAqF4ErF4AqF4AqF3koFnkoFnkoFncnFXooFncnFXUnFXImFXUnFXQnFXAlFG8lFG8l - FGsjE2sjE2kjE2gjE20kFHkoFnImFWEhEl4fEVoeEFwfEVoeEFoeEFgdEFcdEFMcD1UcD1McD04aDk4a - DksZDkkYDUkYDUkYDUsZDkcYDUcYDUIWDEIWDD8VDD8VDEEWDD0UCzwUCz8VDDwUCzgSCjYSCjgSCjYS - CjYSCjgSCjUSCjMRCTMRCTMRCTMRCTEQCTAQCS4QCTAQCS4QCTAQCSwPCCsOCCwPCCwPCCwPCCsOCCkO - CCkOCCcNBykOCCcNBykOCCcNByUNByQMByQMByUNByQMByILBiALBh8LBiALBh8LBh8LBh0KBh0KBh0K - Bh0KBh0KBhsJBRsJBRoJBRoJBRgJBRYHBBYHBBQHBBQHBBQHBBQHBBMHBBMHBBMHBBEGAxEGAxEGAxEG - AxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw8FAw4FAw8FAw4FAw4FAwwFAwwFAwwFAwwFAwwF - AwwFAwoDAgoDAgoDAgoDAgoDAgoDAgoDAgkDAgkDAgcDAgkDAgkDAgkDAgkDAgcDAgkDAgcDAgMBAQMB - AQIBAQIBAQIBAQMBAQMBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////7s9IbY8IbM7ILY8IbE7ILQ7ILM7IK86H605H6w5H6c3Hqo4HqU3HqI2HaI2HZ40HJszHJszHJw0 - HKA1HZszHJszHJkyG5QxG5EwGpQxG5cyG5YyG5EwGo0uGYouGY8wGo8wGoouGYYsGIMsGIUsGIgtGIEr - F4YsGIMsGIAqF3wpFoAqF3koFnooFnooFnkoFnkoFnImFXcnFXcnFXcnFXQnFW8lFG8lFGsjE2sjE2kj - E3ImFXooFnUnFWgjE2MhEl8fEWEhEl8fEV4fEV8fEVcdEFcdEFcdEFUcD1cdEFIbD1IbD04aDkkYDU0a - DkcYDUkYDUkYDUkYDUcYDUYYDUIWDEEWDD0UCz0UCz8VDDwUCzoUCzgSCjoUCzgSCjMRCTYSCjYSCjMR - CTMRCTEQCTAQCTAQCTAQCS4QCTAQCS4QCS4QCS4QCSwPCCsOCCsOCCkOCCkOCCsOCCsOCCkOCCkOCCwP - CCkOCCcNByUNByQMByILBiQMByQMByALBiALBh8LBiALBh8LBiALBh8LBh0KBh0KBh0KBh0KBhsJBRoJ - BRsJBRoJBRYHBBgJBRYHBBQHBBYHBBQHBBMHBBMHBBQHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEG - AxEGAxEGAxEGAw8FAw8FAw4FAw8FAw4FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoD - AgoDAgwFAwoDAgkDAgkDAgkDAgcDAgkDAgkDAgkDAgkDAgcDAgcDAgUCAQUCAQMBAQMBAQMBAQIBAQIB - AQIBAQIBAQIBAQAAAAIBAQIBAQAAAAIBAQAAAAIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////7Q7ILY8IbQ7 - IK86H7g9Ia05H7Y8IbM7IK86H605H6w5H6c3Hqg3Hqc3HqA1HZ40HJw0HJ40HJw0HJkyG5kyG5szHJsz - HJcyG5kyG5IwGpEwGpQxG5IwGo0uGZIwGpEwGpEwGo0uGY0uGYsuGYouGYMsGIMsGIYsGIgtGIAqF4Aq - F4AqF34qF3ooFn4qF3wpFnkoFncnFXUnFXcnFXcnFXQnFXUnFXImFXAlFG8lFG0kFHImFXooFncnFWkj - E2MhEmEhEl4fEV8fEWMhEmEhEl4fEVgdEFcdEFMcD1cdEFMcD1IbD1UcD1AbD00aDkkYDUsZDksZDkYY - DUYYDUcYDUEWDEEWDEEWDEEWDD8VDDwUCzgSCjoUCzgSCjwUCzoUCzoUCzUSCjMRCTMRCTMRCTMRCTMR - CTEQCTMRCTAQCTEQCTAQCS4QCS4QCSwPCC4QCSsOCCwPCCwPCCsOCCwPCCsOCCsOCCcNByUNByQMByUN - ByILBiQMByILBiALBiILBiILBiILBiALBiALBh8LBh8LBh0KBh0KBhsJBRoJBRoJBRsJBRoJBRYHBBgJ - BRQHBBQHBBYHBBYHBBQHBBQHBBQHBBMHBBMHBBEGAxMHBBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw4F - Aw8FAw8FAw8FAw4FAw8FAw4FAw4FAw4FAwwFAwwFAw4FAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgkD - AgkDAgkDAgcDAgcDAgkDAgkDAgkDAgcDAgcDAgcDAgcDAgMBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQAA - AAAAAAIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////7Y8Ibg9Ibg9Ibg9Ia86H7Y8Ia86 - H605H7M7IK86H686H6M2Hag3Hqw5H6U3HqA1HaA1HaI2HZ40HKA1HZ40HJszHJszHJszHJw0HJQxG5Iw - GpcyG5YyG48wGo0uGY0uGZIwGo8wGpEwGosuGYsuGY0uGYsuGYouGYgtGIMsGIErF4MsGIUsGIErF4Er - F3wpFnkoFn4qF34qF3koFnkoFnQnFXkoFnQnFXImFW8lFHImFXQnFXwpFoAqF3AlFGQhEmQhEmQhEmQh - EmQhEl8fEVwfEVcdEFwfEVcdEFcdEFcdEFcdEFMcD1McD1IbD04aDlIbD1AbD0sZDkcYDUsZDkYYDUQW - DEQWDD8VDEQWDD8VDD0UCzwUCzoUCzwUCzoUCzYSCjoUCzYSCjMRCTMRCTMRCTMRCTMRCTEQCTAQCTEQ - CTEQCTEQCS4QCTAQCS4QCSwPCCwPCCsOCCsOCCwPCCsOCCkOCCcNByUNByUNBycNByQMByQMByQMByQM - ByILBiILBiQMByILBiALBh8LBh8LBh8LBh8LBhsJBRsJBRoJBRoJBRoJBRgJBRgJBRYHBBMHBBQHBBQH - BBQHBBQHBBMHBBMHBBMHBBEGAxMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAxEGAw8FAw8F - Aw8FAw8FAw4FAw4FAwwFAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkD - AgcDAgkDAgcDAgcDAgcDAgUCAQUCAQUCAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////7s9Ibk9Ibg9IbQ7ILE7ILg9IbM7IK86H605H686H6w5 - H6w5H6o4Hqg3HqU3Hqc3HqI2HaI2HZ40HJw0HKA1HZw0HJszHJkyG5cyG5kyG5kyG5cyG5IwGpcyG5Ew - GpIwGo8wGpEwGpEwGo8wGo0uGYsuGYouGYsuGY8wGoMsGIYsGIYsGIErF4ErF4gtGIAqF4MsGH4qF34q - F3ooFnooFnooFn4qF3koFncnFXcnFXQnFXUnFYAqF4ErF3AlFGgjE2sjE2YhEmkjE2YhEmYhEl8fEV8f - EV4fEVoeEFcdEFcdEFcdEFUcD1cdEFUcD1McD04aDk4aDksZDksZDksZDkYYDUkYDUcYDUYYDUIWDEIW - DD8VDDwUCz0UCz0UCz0UCzwUCzgSCjgSCjYSCjMRCTUSCjUSCjUSCjMRCTMRCTAQCTAQCTEQCTAQCS4Q - CS4QCSwPCCwPCCwPCDAQCS4QCSsOCCsOCCkOCCcNByUNByUNByUNByUNByUNByQMByILBiILBiQMByIL - BiALBh8LBh8LBh0KBh0KBhsJBR0KBhoJBRoJBRoJBRoJBRgJBRgJBRQHBBYHBBQHBBQHBBQHBBMHBBMH - BBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw4FAw4F - Aw4FAw4FAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgwFAwoDAgoDAgoDAgoDAgkDAgkDAgcDAgkDAgcDAgcD - AgcDAgcDAgcDAgUCAQMBAQIBAQIBAQMBAQIBAQMBAQIBAQAAAAAAAAAAAAAAAAIBAQAAAAAAAAIBAQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////70/Irk9Ibk9Ibs9Ibk9IbQ7ILM7IK86H6w5H686H686H6o4Hq05H6c3Hqc3 - Hqg3HqM2HaM2HaA1HaI2HaA1HZw0HJw0HJkyG5szHJw0HJcyG5cyG5cyG5szHJcyG5kyG5YyG48wGo0u - GY0uGY0uGZIwGo0uGZEwGo8wGo0uGYsuGYgtGIouGYMsGIUsGIMsGIUsGIUsGIAqF34qF4MsGHwpFn4q - F3wpFnooFnUnFXcnFXUnFYMsGIYsGHUnFWsjE2kjE2kjE2YhEmYhEmYhEmQhEmEhElwfEV8fEVoeEFoe - EFcdEFcdEFcdEFMcD1AbD1UcD1AbD1AbD00aDksZDk0aDksZDkYYDUcYDUcYDUQWDEEWDEEWDD0UC0IW - DEEWDD0UCzoUCzgSCjYSCjYSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTMRCTEQCTAQCSwPCCwPCC4QCS4Q - CS4QCSwPCCsOCCsOCCkOCCcNBycNBycNByUNByUNByUNByQMByILBiQMByQMByILBiALBiALBiALBh8L - Bh0KBhsJBRsJBRsJBRsJBRsJBRoJBRoJBRoJBRgJBRYHBBQHBBYHBBQHBBMHBBMHBBMHBBQHBBMHBBEG - AxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw4FAw8FAw4FAwwFAwwFAw4F - Aw4FAwwFAwoDAgoDAgoDAgoDAgwFAwoDAgoDAgoDAgkDAgkDAgkDAgkDAgcDAgkDAgcDAgcDAgcDAgcD - AgMBAQMBAQMBAQIBAQIBAQMBAQIBAQAAAAIBAQAAAAAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////7g9Ib0/Irs9Ibs9Ibg9Ibg9IbM7ILM7IK86H7E7ILQ7IK86H6o4Hqg3HqM2Hac3Hqg3Hqc3HqI2 - HaM2HaA1HaA1HZ40HJw0HJw0HJszHJszHJszHJkyG5szHJkyG5YyG5cyG5YyG48wGpcyG4suGY0uGZEw - GpQxG48wGpcyG5IwGo8wGoouGYouGYsuGYsuGYYsGIMsGIMsGIErF4ErF4AqF3ooFnwpFnwpFnkoFnUn - FXQnFYMsGIouGXwpFm8lFGsjE2sjE2sjE20kFG0kFGkjE2MhEmMhEl4fEV4fEVoeEFwfEVgdEFgdEFcd - EFMcD1McD1IbD1McD1AbD1AbD00aDkkYDUYYDUcYDUkYDUcYDUQWDEYYDUQWDEIWDEEWDDwUCzgSCj0U - CzwUCzYSCjgSCjUSCjUSCjUSCjMRCTMRCTMRCTEQCTEQCTAQCS4QCTEQCTAQCTAQCSwPCCwPCCsOCCwP - CCsOCCkOCCkOCCkOCCcNByUNByUNByUNByQMByQMByQMByILBiILBiALBiALBiALBh0KBhsJBR0KBh0K - BhsJBRsJBRoJBRgJBRoJBRoJBRgJBRYHBBQHBBYHBBQHBBMHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEG - AxEGAxEGAxEGAxEGAw8FAw8FAw4FAw4FAw8FAw4FAw4FAw8FAw4FAwwFAwwFAwwFAwwFAw4FAwwFAwwF - AwoDAgwFAwwFAwoDAgoDAgkDAgkDAgkDAgkDAgkDAgcDAgkDAgcDAgUCAQcDAgcDAgUCAQMBAQMBAQMB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////70/Irg9Ibg9 - Ibs9IbY8IbM7ILQ7IK86H686H7E7IKw5H605H6o4Hqg3HqU3Hqg3HqU3Hqc3Hqc3HqU3HqA1HaA1HZ40 - HJw0HKA1HZszHJw0HJszHJszHJkyG5kyG5kyG5cyG5cyG5YyG5YyG5EwGpIwGpQxG5YyG5QxG5IwGpQx - G5EwGpQxG40uGY8wGpQxG4ouGY8wGogtGIYsGIYsGIgtGIAqF4ErF4AqF34qF3koFnkoFogtGJEwGn4q - F3cnFXAlFG0kFG0kFG8lFG8lFGsjE2gjE2kjE2QhEmEhEmMhEl8fEVwfEVgdEFoeEFcdEFUcD1cdEFUc - D04aDk4aDk0aDksZDksZDkkYDUsZDkkYDUkYDUcYDUcYDUQWDD8VDEEWDDwUCz0UCzoUCzgSCjYSCjYS - CjYSCjUSCjMRCTUSCjMRCTMRCTMRCTEQCTMRCTEQCTAQCTAQCS4QCSwPCC4QCS4QCSsOCCsOCCkOCCkO - CCcNBycNBycNByUNByQMByQMByQMByILBiQMByQMByILBiALBh8LBh8LBh0KBh0KBh0KBhsJBRoJBRoJ - BRgJBRgJBRgJBRYHBBQHBBYHBBQHBBQHBBMHBBMHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEG - Aw8FAw8FAw8FAw4FAw8FAw8FAw8FAw8FAw8FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgoDAgoD - AgoDAgoDAgoDAgkDAgoDAgkDAgcDAgkDAgkDAgcDAgUCAQcDAgUCAQMBAQMBAQIBAQIBAQIBAQMBAQMB - AQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////74/Irs9Ibs9Ibk9Ibk9IbM7ILQ7 - ILQ7ILQ7IK86H605H6w5H6w5H6w5H686H6c3HqM2Hac3HqI2HaU3HqA1HaI2HaI2HZw0HJszHKU3Hpsz - HJ40HJw0HJw0HJkyG5cyG5szHJszHJw0HJszHJcyG5w0HJkyG5cyG5cyG5YyG5cyG5QxG5YyG5IwGpEw - GpQxG40uGZEwGo8wGo0uGYsuGYgtGIUsGIsuGYgtGIMsGH4qF4AqF4ouGZYyG4UsGHkoFnAlFHImFXQn - FXQnFXQnFW0kFGkjE2kjE2QhEmMhEmEhEl8fEV8fEVoeEFgdEFgdEFcdEFcdEFcdEFcdEFcdEFAbD00a - DksZDksZDkcYDUkYDUcYDUYYDUQWDEEWDEEWDD0UCz0UCzoUCzoUCzgSCjYSCjgSCjgSCjYSCjUSCjYS - CjMRCTMRCTMRCTMRCTMRCTAQCS4QCS4QCS4QCSwPCCwPCCwPCCsOCCsOCCkOCCkOCCUNBycNByUNByUN - ByQMByQMByQMByQMByILBiILBiILBiALBiALBh0KBh0KBh0KBhsJBRsJBRsJBRsJBRoJBRgJBRgJBRYH - BBQHBBYHBBQHBBQHBBMHBBMHBBQHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8F - Aw8FAw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgkDAgoDAgoDAgkD - AgkDAgoDAgkDAgkDAgkDAgkDAgcDAgcDAgcDAgIBAQMBAQIBAQIBAQIBAQIBAQIBAQMBAQIBAQIBAQIB - AQAAAAAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////70/Irs9IbM7ILY8IbY8Ibk9IbY8IbQ7ILE7ILE7ILE7 - IKw5H605H6g3Hqw5H6g3Hqo4Hqo4HqI2HaU3HqU3HqM2HaM2HaI2HaM2HZw0HJw0HJszHKI2HZszHJ40 - HKA1HZszHJw0HJw0HJszHJ40HJkyG540HJszHJcyG5cyG5w0HJszHJYyG5QxG5kyG5YyG5QxG5EwGpIw - Go8wGoouGYouGYYsGJEwGo0uGYgtGIgtGIUsGIgtGJkyG5EwGnwpFnwpFnkoFnkoFncnFXImFW8lFGsj - E2sjE2kjE2YhEmEhEmYhEmMhEl4fEV4fEV4fEVoeEFgdEFcdEFcdEFMcD1IbD1AbD1AbD04aDkkYDUkY - DUcYDUcYDUYYDUQWDEIWDEIWDD8VDD0UCz8VDD0UCz0UCzgSCjgSCjUSCjMRCTMRCTMRCTMRCTUSCjMR - CTMRCTAQCTAQCTAQCS4QCS4QCS4QCSwPCCwPCCsOCCkOCCkOCCkOCCcNByUNByUNByUNByUNByQMByQM - ByALBiILBiILBiALBiALBh8LBh0KBh8LBh8LBh0KBhoJBRsJBRgJBRgJBRgJBRgJBRYHBBYHBBYHBBQH - BBMHBBMHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAxEGAxEGAw8FAxEGAw8FAw8FAw4F - Aw4FAw8FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwoDAgkDAgkDAgoDAgoDAgoDAgkDAgoDAgkDAgkD - AgcDAgcDAgkDAgkDAgcDAgMBAQMBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////70/Irk9Ibs9Ibg9IbY8IbY8Ibs9IbY8IbY8IbE7ILE7IKg3Hq05H7E7IKg3 - Hqo4Hqw5H6U3Hqc3HqM2HaM2HaM2HaI2HaM2HZ40HKM2HZ40HKI2HaA1HZ40HKM2HZw0HKA1HZw0HJ40 - HKA1HaA1HZszHJ40HJszHJszHJszHJszHJszHJszHJw0HJszHJkyG5cyG5QxG5QxG5EwGo0uGZEwGo8w - Go0uGZEwGo8wGoouGYouGYsuGZkyG5YyG4YsGI8wGpszHIYsGHkoFncnFXAlFG8lFG0kFGsjE2sjE2gj - E2QhEmMhEmMhEmEhEl4fEVwfEVoeEFcdEFcdEFcdEFUcD1IbD1IbD04aDk4aDk0aDksZDk0aDkkYDUcY - DUIWDEIWDD8VDEEWDD8VDEEWDD8VDDoUCz0UCzgSCjUSCjgSCjYSCjUSCjUSCjYSCjEQCTEQCTAQCTEQ - CTMRCS4QCS4QCTAQCS4QCSwPCCsOCCkOCCkOCCkOCCcNBycNByUNByUNByQMByUNByILBiALBiILBiIL - BiILBiALBh8LBh8LBh8LBh0KBhsJBR0KBhoJBRoJBRgJBRgJBRYHBBYHBBYHBBYHBBMHBBMHBBMHBBMH - BBMHBBEGAxEGAxMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAw4F - Aw4FAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgcDAgcDAgcD - AgkDAgUCAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////70/Irs9Ibs9Ibs9Ibs9Ibg9IbQ7ILY8Ibg9IbM7ILE7ILQ7IK05H605H605H6c3Hqo4Hqg3HqM2 - HaU3Hqo4HqA1HaM2Hac3Hqc3Hqc3HqI2HaM2HaI2Hac3HqI2HaM2HaI2HaI2HaM2HaI2HaU3Hpw0HKA1 - HZ40HJw0HKA1HZcyG540HKI2HZszHJkyG5kyG5w0HJcyG5QxG5YyG5cyG5IwGo8wGpEwGpIwGpIwGo8w - GoouGYouGZ40HKc3HpIwGq86H8A/Iqo4HoouGXooFnkoFnQnFXImFXAlFGsjE2sjE2gjE2QhEmQhEmgj - E2MhEmEhEl8fEVoeEFcdEFwfEVUcD1McD1UcD1AbD1AbD04aDk0aDk0aDkkYDUcYDUcYDUcYDUYYDUIW - DEIWDD8VDEEWDD8VDDwUCzoUCzoUCzYSCjYSCjoUCzUSCjMRCTMRCTMRCTEQCTAQCTAQCSwPCDAQCS4Q - CS4QCSwPCCsOCCwPCCsOCCkOCCkOCCkOCCcNBycNByUNByUNByUNByILBiILBiILBiALBiALBiALBh8L - Bh8LBh0KBhsJBR0KBhoJBRoJBRoJBRgJBRYHBBYHBBYHBBYHBBQHBBQHBBQHBBMHBBMHBBEGAxMHBBMH - BBEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAxEGAw8FAw8FAw4FAw8FAw8FAw4FAw8FAw4FAwwFAwoDAgoD - AgwFAwoDAgoDAgoDAgoDAgoDAgoDAgoDAgoDAgkDAgcDAgcDAgkDAgkDAgcDAgcDAgkDAgUCAQIBAQMB - AQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////7s9Ibs9Ib0/ - Ir4/Irk9IbQ7ILY8IbM7ILs9Ia86H7M7ILM7IKg3Hqo4Hqc3Hq86H6o4Hqw5H6g3Hqw5H6o4Hqo4HqU3 - Hqc3Hqc3Hqg3Hqc3HqU3HqM2HaU3HqU3HqA1HaM2HaA1Hag3HqI2HaA1Hac3HqA1HaA1HZw0HJszHJw0 - HKI2HaM2HaA1HaI2HZszHJ40HJszHJszHJkyG5kyG5szHJYyG5YyG5QxG5QxG48wGpEwGo0uGZw0HKw5 - H686H8xDJNpPMMpDJKM2HYouGX4qF3koFnQnFXImFXAlFHImFWsjE2kjE2gjE2YhEmYhEmMhEl8fEVoe - EF8fEVwfEVoeEFUcD1cdEFMcD1AbD1cdEFIbD0kYDUsZDkcYDUcYDUcYDUcYDUQWDEIWDEEWDD8VDD8V - DDwUCzgSCjgSCjYSCjoUCzoUCzMRCTMRCTYSCjYSCjMRCTMRCTAQCTAQCS4QCS4QCS4QCTAQCSsOCCwP - CCwPCCkOCCkOCCkOCCkOCCcNBycNBycNByUNByQMByILBiILBiALBiALBiALBh0KBh0KBh0KBh0KBh0K - BhsJBRoJBRoJBRgJBRYHBBgJBRgJBRYHBBQHBBQHBBQHBBQHBBMHBBEGAxMHBBMHBBEGAxEGAxEGAxEG - AxEGAxEGAw8FAxEGAxEGAw8FAw8FAw4FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoD - AgwFAwoDAgoDAgoDAgoDAgkDAgcDAgcDAgkDAgkDAgcDAgcDAgkDAgcDAgMBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////70/Irk9Ibk9Ib0/IrY8Ibk9Ibg9 - IbQ7ILk9Ia05H7E7IK05H7M7IKw5H6w5H6o4Hqw5H6o4Hqo4Hqg3Hq05H6g3Hqo4Hqo4Hqc3Hqw5H6I2 - HaU3HqI2HaI2HaU3HqU3HqI2HaI2Hac3HqU3Hqo4HqU3HqU3HqM2HaM2HaM2HaA1HaI2HaM2HaU3HqU3 - HqA1HaM2HZw0HJszHJszHJkyG5kyG5kyG5kyG5kyG5kyG5YyG5IwGpcyG6c3Hr0/IsRBI9tSM95gQ9pP - MLk9IZszHIgtGH4qF3ooFnooFncnFXImFXQnFXAlFGsjE2kjE2YhEmMhEl8fEWEhEl8fEV4fEVcdEFcd - EFcdEFMcD1McD1IbD1IbD04aDk4aDksZDkYYDUkYDUcYDUQWDEQWDEQWDD8VDD8VDDwUCzoUCz8VDDoU - CzoUCzgSCjoUCzUSCjUSCjUSCjMRCTMRCTEQCTEQCTAQCTAQCS4QCS4QCTAQCSsOCCsOCCkOCCsOCCkO - CCcNByUNByUNByUNByUNByQMByILBiILBiALBh8LBiALBiALBiALBh8LBh0KBh0KBhoJBRoJBRoJBRoJ - BRgJBRgJBRgJBRYHBBQHBBQHBBQHBBQHBBMHBBMHBBQHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8F - Aw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAwwFAw4FAw4FAw4FAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgoD - AgkDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgkDAgcDAgUCAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////74/Irs9Ib0/Irk9Ibk9Ibk9Ibk9Ibg9IbM7ILQ7ILQ7 - ILM7ILM7ILE7IKo4Hqo4Hqo4Hqw5H6w5H6w5H605H6w5H686H605H605H6w5H6o4Hqw5H6c3HqM2HaM2 - Hag3Hqc3HqU3Hq05H6w5H6o4Hqc3Hqc3Hqg3HqU3Hqc3Hqw5H6M2Hac3Hqo4Hqc3HqM2HaA1HaI2HaA1 - HZ40HJ40HJw0HJszHJszHJkyG5szHJkyG5kyG5w0HK86H8lCJNpLK91cP+BrUN1cP8pDJKc3Ho0uGYAq - F4ErF34qF3koFnooFncnFXQnFW8lFG0kFGgjE2kjE2QhEmEhEl8fEV8fEVwfEVoeEFcdEFwfEVcdEFMc - D1IbD1AbD1AbD0sZDkkYDU0aDkYYDUcYDUQWDEEWDEEWDEIWDEEWDD8VDD8VDDoUCzgSCjwUCzYSCjYS - CjMRCTMRCTMRCTMRCTEQCTEQCTAQCTAQCS4QCSwPCC4QCS4QCSsOCCsOCCsOCCkOCCkOCCkOCCcNByUN - ByUNByUNByILBiILBiALBiALBiILBiALBh8LBh0KBh0KBh0KBhoJBRoJBRgJBRoJBRoJBRgJBRYHBBYH - BBQHBBQHBBQHBBYHBBMHBBMHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw4F - Aw4FAw4FAwwFAwwFAwwFAw4FAw4FAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgoDAgkDAgkDAgkD - AgkDAgkDAgkDAgcDAgcDAgcDAgUCAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////74/Ir0/Irg9Ibg9Ibg9IbQ7ILY8Ibg9Ibk9IbY8Ia86H7Q7ILQ7IK05H6o4 - Hqw5H605H605H7E7IKw5H605H686H686H6w5H6w5H6o4Hqo4Hqw5H6g3Hq05H605H6g3Hqg3Hqc3Hqg3 - Hqo4Hq86H6w5H6g3Hqo4Hqo4Hqc3Hqc3HqM2HaI2Hao4Hqw5H6o4HqM2HaA1HaM2HaM2HaI2HaI2HZ40 - HJ40HJw0HJ40HJw0HJszHKA1HbY8IdlJKdxXOeBqT+FuVN5iRtlIJ7Q7IJszHI8wGoErF4ErF4AqF34q - F3ooFncnFXUnFW8lFGsjE2gjE2gjE2MhEmMhEmEhEmEhEloeEFgdEFgdEFoeEFUcD1IbD1IbD1IbD1Ab - D04aDk4aDkcYDUYYDUQWDEQWDEIWDEIWDEQWDEQWDD8VDDwUCzoUCzoUCzwUCzgSCjUSCjYSCjMRCTEQ - CTEQCTEQCTEQCTAQCSwPCC4QCSwPCCwPCCwPCCsOCCsOCCsOCCsOCCkOCCkOCCcNByUNByUNByQMByQM - ByILBiALBiALBh8LBh8LBh8LBh8LBh8LBh0KBhsJBRoJBRoJBRoJBRgJBRgJBRgJBRgJBRYHBBQHBBYH - BBQHBBMHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw8FAw4FAwwF - Aw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgcD - AgcDAgcDAgcDAgMBAQIBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////70/Ir0/IrY8IbY8Ibk9IbY8IbY8IbY8Ibs9IbY8IbQ7ILE7ILM7IKo4Hqw5H686H6o4Hq05H686 - H605H7E7ILM7ILE7IK86H605H6g3Hqw5H6o4Hqw5H605H7E7IKg3Hqg3Hqg3Hq05H6o4Hqw5H6w5H6o4 - Hq05H605H686H6o4Hqo4Hqg3Hqo4Hqw5H6g3Hqo4Hqw5H6g3Hqg3Hqc3Hqg3HqM2HaM2HaA1HaA1HaA1 - HaA1Hac3Hr0/IttRMt1eQeBoTeFvVd9lStpOLsJAI6g3HpcyG4suGYMsGIErF4ErF4AqF3wpFnooFnoo - FncnFW8lFGkjE2YhEmYhEmEhEl4fEV8fEV4fEV8fEV4fEVcdEFUcD1cdEFIbD04aDlAbD00aDk0aDkYY - DUcYDUYYDUQWDEYYDUcYDUEWDDwUCz0UCzoUCzwUCzwUCzgSCjYSCjUSCjMRCTMRCTMRCTAQCTEQCTAQ - CS4QCS4QCSwPCDAQCS4QCSwPCCsOCCsOCCsOCCsOCCkOCCkOCCQMByUNByUNByUNByQMByILBiILBiAL - BiALBh8LBh0KBh0KBh0KBhsJBRoJBRoJBRoJBRgJBRYHBBYHBBgJBRgJBRYHBBYHBBQHBBMHBBMHBBEG - AxMHBBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAw4FAw4FAwwF - AwwFAwwFAwwFAwwFAwwFAwkDAgkDAgoDAgkDAgoDAgkDAgkDAgkDAgcDAgkDAgkDAgcDAgcDAgcDAgMB - AQIBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8A/Ir4/Ir0/ - Irs9IbQ7ILk9IbY8Ib0/IrQ7ILk9Ibk9IbQ7ILQ7IK05H686H7M7IK86H7E7ILE7ILE7ILE7ILE7IK86 - H605H6w5H6w5H6w5H605H686H7E7IK86H7E7ILE7IK86H7E7IK86H686H7E7ILE7IK05H6w5H6w5H686 - H605H6o4Hq86H686H7E7IK05H6o4Hq86H605H6c3Hqw5H605H6o4Hqc3HqU3Hqw5H6o4HrE7IMVBI9xY - OuBoTeFuVOJ0W+BoTdxXOdFFJbE7IJw0HJIwGogtGIUsGIYsGIMsGIErF3ooFnooFncnFXUnFXAlFG0k - FGsjE2EhEmYhEmMhEl8fEVwfEVoeEFgdEFcdEFcdEFcdEFUcD1AbD00aDk4aDksZDkkYDUcYDUYYDUQW - DEQWDEIWDD8VDEEWDD0UCzgSCjwUCzgSCjgSCjYSCjYSCjYSCjUSCjMRCTMRCTMRCTAQCTAQCTAQCS4Q - CS4QCS4QCS4QCSsOCCsOCCkOCCkOCCcNByUNByUNByQMByQMByUNByQMByILBiALBiALBh8LBh8LBh0K - Bh0KBhsJBRoJBRoJBRoJBRoJBRgJBRYHBBgJBRYHBBYHBBgJBRQHBBMHBBMHBBMHBBMHBBEGAxEGAxEG - AxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw4FAw4FAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwF - AwoDAgkDAgkDAgoDAgkDAgkDAgoDAgkDAgoDAgkDAgkDAgcDAgcDAgkDAgcDAgMBAQIBAQIBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////70/Irg9Ibs9Ibs9Ibk9Ibs9Ibg9 - Ibg9Ibg9IbQ7ILk9Ibk9Ibs9IbQ7ILQ7ILE7ILE7ILM7IK86H7Y8Ia05H605H605H6w5H7Q7ILE7IK86 - H7M7ILY8IbM7IK05H7M7IK05H7M7ILM7IK86H7M7ILQ7ILE7IK86H686H7M7ILE7ILY8Ia86H7E7ILE7 - ILg9IbQ7ILE7IK05H605H6w5H7Q7ILM7IKw5H6w5H605H7E7ILE7ILY8Ic5EJd1eQeFuVOFuVOJyWeBs - Ut5gQ9hHJr0/IqM2HZszHI0uGYouGYYsGIUsGIgtGIAqF4AqF3koFnkoFncnFXImFWsjE2gjE2YhEmQh - EmgjE2EhEloeEFcdEFcdEFgdEFUcD1UcD1McD1AbD0sZDksZDk0aDksZDksZDkcYDUQWDEIWDEEWDD0U - Cz0UCzgSCjoUCzoUCzgSCjYSCjYSCjYSCjUSCjUSCjMRCTMRCTEQCTEQCTAQCTEQCS4QCSwPCCwPCCkO - CCkOCCkOCCkOCCcNBycNByUNByQMByQMByQMByQMByILBiALBiILBh8LBiALBh8LBh8LBh0KBhsJBRsJ - BRgJBRgJBRgJBRoJBRgJBRgJBRYHBBgJBRYHBNXV1P////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6WkpAMBAQIBAQIBAQMBAQMBAQIBAQAAAAIB - AQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////7k9Ibs9Ib0/Ir0/Irg9Ibk9IbQ7ILQ7ILE7ILQ7ILs9 - IbQ7ILg9Ibg9Ibg9IbM7ILM7ILM7ILE7ILM7IK05H6o4Hq86H6w5H686H7E7ILE7IK86H7Q7ILY8IbY8 - IbE7ILQ7ILQ7ILE7ILM7IK05H7M7ILE7ILM7ILQ7ILM7ILY8Ibg9IbM7ILQ7IK86H7s9Ib0/IrY8IbQ7 - IL0/IrY8IbY8Ibk9IbQ7ILM7ILM7ILY8Ib0/IsJAI9ZGJt9kSOJyWeJyWeN3XuFwV99lStpNLcRBI7E7 - IKI2HZkyG48wGo0uGYouGYgtGIMsGIErF4AqF3wpFnkoFnkoFncnFXAlFG8lFGkjE2kjE2kjE1wfEV8f - EVoeEFgdEFgdEFMcD1IbD1AbD1AbD1AbD04aDk0aDk0aDkQWDEYYDUQWDEQWDEEWDEQWDD8VDDwUCzwU - CzwUCzoUCzgSCjUSCjUSCjUSCjMRCTEQCTEQCTAQCS4QCS4QCS4QCS4QCS4QCSsOCCkOCCkOCCkOCCkO - CCkOCCUNByUNByQMByQMByUNByQMByILBiALBh8LBiALBh8LBh8LBh0KBh0KBh0KBhsJBRsJBRoJBRoJ - BRoJBRgJBRYHBBgJBRYHBF1YV/////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////6WkpAMBAQIBAQIBAQMBAQMBAQIBAQAAAAIBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////74/Ir4/Irg9Ibk9IbY8Ibk9IbY8Ibg9Ibs9Ibg9IbY8IbY8Ibg9IbY8Ibk9 - IbM7ILg9IbE7IK86H7E7ILM7IK86H7E7IK05H7Q7ILM7ILE7ILM7ILY8Ibg9IbM7ILE7ILQ7ILM7ILQ7 - ILM7ILg9Ibk9Ibg9Ibg9Ibs9IbM7ILY8Ibk9IbY8Ibg9IbY8Ibg9Ib4/Ir0/Irk9Ibg9IbQ7ILY8Ibs9 - IbY8IbQ7ILM7ILY8IcJAI8xDJNpNLeBrUON3XuN4YON7Y+N3XuBrUNxXOdNGJrk9Iag3Hp40HJkyG5cy - G40uGY0uGYgtGIgtGIMsGHwpFnooFnkoFnkoFnQnFXAlFG0kFGkjE2kjE2QhEloeEF4fEVwfEVoeEFcd - EFIbD1IbD1McD1McD1IbD0sZDk0aDkYYDUQWDEcYDUYYDUQWDEEWDEEWDEEWDD0UCz0UCzoUCzYSCjUS - CjYSCjMRCTMRCTMRCTMRCTEQCTAQCTAQCTAQCS4QCSwPCCsOCCsOCCkOCCkOCCkOCCkOCCkOCCUNByUN - ByQMByQMByILBiILBiALBiALBiALBh8LBh8LBh8LBh0KBhsJBRsJBRsJBRoJBRgJBRoJBRgJBRYHBBYH - BBQHBBQHBL+9vf////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6WkpAMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////7Y8Ibs9Ibg9Ibk9Ibk9IbY8Ibk9Ibk9Ib0/Ir0/IrY8IbQ7ILQ7ILY8IbY8IbY8IbM7ILY8Ia86 - H7M7ILM7ILM7ILM7ILQ7ILY8IbQ7ILQ7ILg9IbY8Ibg9IbQ7ILg9IbQ7ILY8IbM7ILQ7ILg9Ibs9Ibg9 - Ib0/Irg9Ib0/Ir0/Ir0/Irs9Ibs9Ib4/Irk9IcA/IsVBI8JAI70/Ir0/IrY8Ibs9IcA/Irk9Ibk9IcdC - JM9EJdhHJtxXOeBrUOR8ZeR8ZeN7Y+N7Y+JyWd1eQdlIJ8A/IrQ7IKg3Hp40HJszHJQxG5IwGo0uGYYs - GIUsGIUsGIErF3wpFn4qF3koFnkoFnAlFG8lFG0kFGMhEl4fEV4fEV8fEVoeEFwfEVcdEFMcD1McD1Uc - D1McD0sZDk0aDksZDkcYDUYYDUYYDUYYDUEWDEIWDD0UCzwUCz0UCzwUCzgSCjYSCjMRCTMRCTUSCjMR - CTMRCTMRCTAQCTAQCTEQCS4QCSwPCCwPCCwPCCwPCCsOCCsOCCkOCCUNBycNBycNByUNByQMByILBiIL - BiILBiILBiILBiALBiALBiALBh0KBhsJBRoJBRsJBRoJBRgJBRgJBRgJBRYHBBYHBBQHBBQHBEhDQv// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////6Wk - pAMBAQMBAQIBAQIBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////70/Ir0/Ir0/ - Ir0/Irk9Ibs9Ibg9Ibk9IbY8Ibg9Ibs9Ibk9Ibg9IbY8IbQ7ILY8Ibk9IbY8IbE7ILQ7ILY8Ia05H7Y8 - IbY8IbY8Ibk9IbY8Ibk9IbY8IbY8Ibg9IbY8Ibg9Ibg9Ibk9IbY8Ib0/Ir0/Irs9IcA/Irs9Ib4/Ir0/ - Ir4/IsA/Ir4/Ir4/Ir4/IsVBI8dCJMVBI8JAI74/IsRBI74/IsdCJMJAI8dCJMxDJNpNLdpPMN1aPOFw - V+WDbeWCbOWDbeR/aOJ1XN9lSttSM8pDJMA/IrM7IKM2HaI2HZw0HJYyG5IwGo0uGYouGYUsGIYsGIEr - F34qF4AqF3koFnUnFXUnFWsjE2gjE2EhEmQhEmEhEloeEFcdEFcdEFcdEFcdEFMcD1IbD1McD00aDk0a - DkkYDUcYDUYYDUYYDUYYDUIWDEEWDD0UCz0UCzwUCzgSCjYSCjYSCjYSCjMRCTMRCTMRCTEQCTAQCS4Q - CS4QCTAQCSwPCCwPCCsOCCsOCCsOCCkOCCkOCCkOCCkOCCkOCCUNByQMByQMByILBiILBiILBiILBiAL - Bh8LBh8LBh8LBh0KBhsJBR0KBhsJBRoJBRoJBRoJBRYHBBgJBRYHBBQHBBQHBLOxsf////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////6WkpAUCAQMBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////70/Irs9Ibk9Ib4/Ir0/Irk9Ibg9 - Ibk9Ibg9Ibk9Ib0/IrQ7ILk9Ibs9Ibk9IbM7ILY8IbM7ILk9Ibg9Ibs9Ibk9Ibg9IbY8IbQ7ILs9Ibk9 - Ib0/IrY8Ibk9Ibs9IbY8Ib0/Ir4/Irs9Ib0/Ir0/Ir0/IsA/IsA/IsJAI8A/Ir4/IsRBI8JAI8JAI8VB - I70/IsRBI8dCJMlCJMdCJMRBI8lCJMlCJMxDJM5EJc9EJdFFJdtRMtxYOt5iRuJ0W+aJdOaHcuaFb+WC - bOR8ZeFvVd1bPtlJKcpDJL0/Iq86H605H6g3HqA1HZszHJkyG5EwGo8wGo0uGYYsGIMsGIErF3wpFncn - FXcnFXImFW8lFGQhEmQhEl4fEV8fEVwfEVwfEVoeEFoeEFcdEFAbD1IbD04aDk0aDksZDkcYDUcYDUcY - DUQWDEQWDEIWDD0UCzoUCzwUCzoUCzgSCjoUCzUSCjUSCjUSCjMRCTMRCTEQCTAQCS4QCS4QCS4QCS4Q - CS4QCSwPCCwPCCkOCCkOCCcNBycNByUNByQMByUNByQMByQMByILBiILBiILBiILBiALBh8LBh0KBh0K - BhsJBRsJBRsJBRoJBRoJBRoJBRgJBRgJBRYHBBYHBBQHBDIrKfX19f////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6WkpAUCAQMBAQIBAQMBAQMBAQMBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////7g9Ibs9Ib0/Irs9Ib0/Ir0/Irk9Ibs9Ibs9Ibk9Ib0/ - IrY8Ibk9IbY8IbQ7ILg9IbM7ILQ7ILM7ILY8IbQ7IL0/Irk9Ib0/Irk9Ibs9IbQ7ILs9Ibk9Ibk9Ib0/ - Irk9Ib4/Ir0/Ir4/IsJAI74/Ir0/Ir0/IsJAI8JAI8RBI8dCJMdCJMdCJMpDJMpDJMdCJMdCJMdCJMlC - JMpDJMxDJM9EJc9EJcxDJNZGJtlIJ9pNLdxVN95gQ+BrUOR8ZeeNeeeNeeaHcuaHcuWBauJ0W95gRNtU - NdNGJsdCJL0/IrQ7ILM7IKo4Hqc3HqU3Hp40HJszHJQxG48wGosuGYYsGH4qF3koFnkoFnQnFXImFW8l - FGkjE2YhEmQhEloeEFwfEVwfEVgdEFgdEFUcD1IbD1IbD1IbD00aDk0aDkkYDUkYDUcYDUQWDEIWDEEW - DEEWDD0UCzwUCzgSCjoUCzoUCzgSCjYSCjMRCTUSCjMRCTAQCTAQCTEQCS4QCS4QCTAQCSwPCCwPCCsO - CCkOCCcNByUNByUNByUNByUNByQMByQMByQMByILBiILBiALBiALBh0KBh0KBh8LBh0KBhsJBRoJBRsJ - BRoJBRYHBBgJBRgJBRYHBBYHBBQHBBQHBKelpP////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////6WkpAUCAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////70/Irk9Ib0/Ir0/Ir0/Ir0/Irs9Ib0/Ir0/Irk9Ibs9Ibs9IbY8IbQ7ILk9 - Ibs9Ibg9IbY8Ibs9Ibk9Ibg9Ibg9Ibg9Ibs9Ib0/Irk9IcA/Irk9Ib0/Ir0/Ir0/Ir4/IsA/Ir4/Irs9 - IclCJMJAI74/IsJAI8RBI8RBI8lCJMdCJMlCJMpDJMxDJM5EJclCJMpDJMpDJNFFJdhHJthHJtlIJ9lI - J9lIJ9pNLdpOLtpPMNxYOuBqT+FwV+R/aOeNeeiRfeeNeeeMd+aHceN5YeBqT91bPtpOLtNGJsRBI70/ - Irg9IbE7IKw5H6o4HqU3HqI2HZszHJkyG5IwGo0uGYYsGIAqF3wpFnkoFnUnFXImFXAlFGYhEmgjE2Mh - EmMhEloeEFcdEFcdEFcdEFIbD1McD1AbD00aDk0aDkkYDUkYDUQWDEQWDEIWDEEWDEQWDD8VDDoUCzgS - CjgSCjgSCjwUCzYSCjUSCjMRCTMRCTEQCTEQCTMRCTAQCTEQCTAQCS4QCSwPCCsOCCkOCCkOCCkOCCcN - BykOCCUNByUNByQMByQMByILBiALBiALBh8LBh0KBh0KBh8LBh0KBhsJBRoJBRsJBRgJBRYHBBgJBRoJ - BRgJBRYHBBQHBBYHBDIrKfX19f////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6WkpAcDAgMBAQIBAQIBAQMBAQMBAQMBAQIBAQMBAQIBAQAAAAIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////8A/Ir4/Ir0/Ir4/Ir0/Ir4/Irs9Ibs9Ib0/Irk9Ibk9Ibk9Ibg9Ibk9Ibk9Ibk9Ib0/IrY8Ibs9 - IbY8Ibg9IbY8Ibs9Ib0/Ir0/Irs9Ib0/Irg9Ib0/Ir4/IsJAI70/IsA/IsJAI8JAI8VBI74/IsVBI8VB - I8dCJMxDJMlCJMlCJMdCJM9EJc5EJc9EJdFFJdZGJthHJtlIJ9hHJtpOLtpLK9pOLtpLK9tUNdxXOd1b - Pt5iRuJyWeN5YeaFb+mXhemXheiSfueNeeeLduR9ZuFwV95gRNtUNdlJKdVGJslCJMA/IrQ7ILM7IK05 - H6U3Hqc3HqM2HaM2HZw0HJcyG5EwGogtGIAqF3wpFnUnFXUnFXQnFW8lFGsjE2kjE2QhEl8fEVwfEVcd - EFcdEFUcD1McD04aDk0aDk0aDkkYDUsZDkcYDUkYDUQWDEYYDUYYDUQWDD8VDDoUCzoUCzUSCjYSCjUS - CjYSCjYSCjUSCjAQCTEQCTEQCTAQCSwPCCwPCCwPCCwPCCsOCCkOCCkOCCkOCCkOCCcNByUNByQMByQM - ByQMByQMByILBiALBiALBh8LBh8LBh0KBh8LBhsJBRoJBRoJBRoJBRYHBBgJBRoJBRgJBRYHBBQHBBYH - BBYHBIyJif////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////6Wk - pAcDAgMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8JAI70/Ir0/ - Ir4/Ir0/Ir4/Ir0/Irs9Ib0/Irk9Ib0/Irg9Ibs9Ibg9Ibk9Ib0/Irk9IbQ7ILk9Ibs9Ib4/Irk9IbY8 - Ibk9Ib4/Irs9Ib4/Ir0/Ir4/IsJAI8JAI74/Ir0/IsA/IsA/IsVBI8dCJMdCJMlCJMxDJMxDJMpDJNFF - JcxDJNFFJdFFJdZGJthHJtlJKdpNLdpLK9pOLttRMttRMtpPMNtUNdxVN91aPN5gROBqT+N4YOWCbOaH - cumXheqaiOmWg+iSfueNeeaFb+N3XuBrUN1cP9pOLtlIJ9NGJsRBI74/Ir0/IrQ7ILQ7IKw5H6o4Hqc3 - HqM2HZszHJszHJEwGogtGIErF3wpFnkoFncnFXImFW8lFGQhEmMhEmEhEl4fEVgdEFgdEFcdEFUcD1Mc - D00aDk4aDkkYDUkYDUsZDkkYDUcYDUYYDUQWDEEWDEIWDD0UCz0UCzoUCzoUCzUSCjYSCjUSCjMRCTMR - CTAQCTAQCTEQCTAQCS4QCS4QCS4QCSwPCCwPCCsOCCkOCCcNByUNByQMByQMByQMByQMByILBiILBiIL - BiALBiALBh8LBh0KBh0KBhsJBR0KBhsJBRoJBRoJBRgJBRgJBRgJBRYHBBQHBBQHBBYHBBQHBOvq6v// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////6WkpAcDAgMBAQMBAQMB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////74/Ir0/Ir0/IsRBI7s9Ib4/Ir0/ - Ir0/Irs9Ibk9Ib0/Irk9Ibk9Ibg9Ib0/Ir0/Irk9IbQ7ILk9Ibk9Ibs9Ibk9Ib0/Ir4/Ir0/Irk9IcVB - I8A/IsJAI8JAI8JAI8JAI8RBI8A/IsJAI8lCJMlCJM5EJc5EJc5EJc5EJcxDJM9EJdZGJtVGJtZGJtlI - J9pLK9pNLdtRMttRMttRMttUNdxXOdtSM9xYOt1eQd9mS+BrUOFwV+R9ZuaHcuiRfeqciuufjuqZh+mW - g+iSfuaHceR8ZeFvVd1eQdxVN9pLK9NGJs9EJcRBI8VBI7g9Ia86H7Q7ILM7IK05H6c3HqM2HZw0HJcy - G40uGYouGYErF4AqF3koFnkoFnAlFGsjE2kjE2MhEmEhEl4fEVoeEFcdEFcdEFUcD1McD1IbD00aDksZ - DkkYDUkYDUQWDEYYDUEWDD8VDEIWDEEWDD8VDDoUCzoUCzYSCjMRCTUSCjMRCTMRCTEQCTMRCTAQCS4Q - CTAQCS4QCS4QCSsOCCsOCCsOCCkOCCkOCCcNByUNByUNByUNByUNByILBiILBiILBiILBiILBiALBh8L - Bh8LBh0KBh0KBhsJBRoJBRsJBRoJBRgJBRYHBBYHBBYHBBQHBBgJBRYHBH16ef////////////////// - /////////////////////////////8rJyQ4FAw4FAw8FAw4FAwwFAwwFAwwFAw4FAwoDAgoDAgoDAgoD - AgwFAwoDAgoDAgoDAgkDAgkDAgcDAgkDAgkDAgcDAgkDAgkDAgcDAgUCAQMBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////70/IsJAI8A/IsJAI8A/Ir0/Irk9Ib0/Irk9Ibs9Ib0/ - Irs9Ib0/Ir0/Ir0/Irs9Ib0/IrY8Ibk9Ibg9Ib0/Irs9Ib0/Ir4/Irs9Ib0/Ir0/IsJAI8JAI8A/IsVB - I8dCJMxDJMdCJMlCJM5EJcxDJMxDJMpDJM5EJdNGJtFFJdNGJtZGJtpLK9pLK9pOLtpPMNpOLttSM9xY - Ot1aPN1eQd1cP91cP91eQd5iRuBrUOFwV+N4YOWDbeeNeemTgOufjuyklOufjumXhemVgueLduWCbOJ0 - W+BoTd1cP9tSM9lIJ9VGJsxDJMlCJMA/Irs9Ibg9IbQ7ILE7IKw5H6M2HaM2HZ40HJkyG5cyG40uGYUs - GH4qF3UnFXcnFW8lFGYhEmgjE2EhEl8fEVwfEVoeEFcdEFUcD1McD1IbD1IbD00aDksZDksZDkkYDUYY - DUIWDEIWDD8VDD0UCz0UCz0UCzoUCzgSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTEQCTAQCTAQCTAQCSwP - CCsOCCkOCCcNBysOCCsOCCUNByUNByUNByUNByQMByILBiILBiILBiALBiALBh8LBh8LBh8LBh8LBhoJ - BRoJBRoJBRgJBRoJBRgJBRYHBBQHBBQHBBYHBBQHBBMHBNXV1P////////////////////////////// - /////////////////2xpaA4FAw4FAw4FAw4FAwwFAwwFAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgkD - AgkDAgkDAgkDAgoDAgkDAgkDAgcDAgkDAgcDAgUCAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////74/IsRBI74/Ir0/IsRBI70/Irk9Ib0/Ir0/Ir0/Ir0/Ir0/Ir0/Irk9Ibs9 - Ibk9Ib0/Irs9Ibs9Ib0/Ir4/Ir4/IsA/Ir0/Ir4/Ir4/Ir0/IsRBI8RBI8VBI8dCJMxDJMlCJMdCJMRB - I8xDJM5EJdFFJc5EJdZGJtZGJtlIJ9VGJtpLK9pPMNpNLdtUNdxVN9xYOtxYOt1bPt1bPt1cP95iRuBo - Td9lSuBqT+FuVOR8ZeR/aOeLduiRfemWg+yike2omeyikeuejOqaiOmTgOaHcuR8ZeFuVN9mS91bPttR - MtlIJ9NGJs9EJclCJL0/IsRBI7s9IbQ7ILQ7IKw5H6M2HZ40HJszHJszHJIwGosuGYMsGHkoFnkoFnIm - FW0kFGgjE2gjE18fEVwfEVoeEFcdEFgdEFUcD1McD00aDk4aDksZDk0aDk0aDkcYDUQWDEQWDEEWDD0U - Cz8VDD0UCzoUCzoUCzgSCjUSCjYSCjUSCjMRCTMRCTMRCTEQCTEQCTAQCS4QCS4QCSwPCCwPCCsOCCsO - CCcNBysOCCcNByUNByUNByQMByQMByILBiILBiALBh8LBh8LBh8LBh0KBh0KBhsJBRoJBRoJBRgJBRoJ - BRgJBRYHBBYHBBYHBBYHBBMHBBMHBG1qaf////////////////////////////////////////////// - /9XU1A4FAw4FAw4FAw4FAwwFAw4FAw4FAwwFAwwFAwoDAgoDAgwFAwoDAgoDAgoDAgoDAgkDAgkDAgoD - AgkDAgkDAgcDAgkDAgcDAgUCAQMBAQIBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////8lCJMVBI8RBI70/Ir4/IsRBI74/Ir0/Ir0/Ir0/Ir0/Irk9Ibk9Ib0/Ir4/Ir0/Ir0/Ir0/Ir0/ - Ir4/Ir4/Ir0/IsVBI8JAI8A/IsRBI70/IsdCJMdCJMVBI8VBI8dCJMxDJM5EJc5EJdNGJtZGJtlIJ9lI - J9lJKdlIJ9hHJtlJKdpOLttSM9xYOt1cP9xYOtxXOd1eQd1cP95gQ99kSN9lSuBsUuBsUuFvVeN3XuR9 - ZuaHceeMd+mWg+uejOyklO6sneymluyklOufjumXheaJdOWDbeN4YOBsUt5gRNxVN9pOLtpNLdlIJ89E - JcpDJMJAI70/Ir0/Irk9Ia86H6g3HqA1HaA1HZw0HJszHJEwGoYsGIAqF3koFnQnFW0kFG0kFGsjE18f - EV4fEVgdEFwfEVcdEFUcD1IbD1IbD04aDk0aDksZDkcYDUYYDUQWDEQWDEIWDD8VDEEWDDoUCzwUCzwU - CzUSCjYSCjYSCjUSCjUSCjMRCTUSCjMRCTAQCTAQCS4QCS4QCSwPCCwPCCkOCCsOCCkOCCkOCCkOCCcN - BycNByQMByQMByILBiQMByALBh8LBh8LBh8LBh8LBh8LBhsJBRoJBRsJBRoJBRgJBRgJBRYHBBgJBRYH - BBQHBBMHBBMHBBQHBNXV1P///////////////////////////////////////////////2xpaA4FAw4F - Aw4FAwwFAwwFAw4FAwwFAwwFAwoDAgoDAgoDAgwFAwwFAwoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgkD - AgkDAgcDAgMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8JAI8dCJMlC - JMdCJMpDJMRBI74/Ir4/Irs9Ib0/Ir0/Irk9IbY8Ib0/Irs9Ib4/Ir0/Ir0/Ir4/IsJAI70/Ir0/IsJA - I70/IsJAI74/IsVBI8JAI8dCJMlCJMpDJM5EJcxDJNFFJdFFJdVGJtFFJdZGJtZGJthHJtlJKdpOLtpO - LttUNd1aPNxYOt1bPt1cP95gQ95gRN9kSOBoTeBqT+BrUOJyWeN7Y+N4YON5YeaHcueMd+mWg+qciuyi - ke2omO+ypO6uoOymluyklOufjuiSfuaHcuN7Y+JyWeBqT99kSNxVN9pPMNpOLtlIJ85EJcpDJMRBI8JA - I70/IrE7IK05H6c3HqU3Hpw0HJszHJcyG40uGYYsGH4qF3wpFnImFWsjE20kFGQhEmQhEl4fEVgdEFcd - EFgdEFIbD1McD1IbD04aDk0aDksZDkkYDUYYDUIWDEQWDEIWDEQWDEEWDD8VDDwUCzgSCjYSCjUSCjUS - CjUSCjMRCTMRCTEQCTAQCTAQCS4QCSwPCCsOCCwPCCwPCCsOCCsOCCcNBykOCCcNByUNByUNByQMByIL - BiILBiALBiALBh8LBh0KBh8LBh8LBhsJBRsJBRsJBRoJBRgJBRgJBRYHBBYHBBYHBBQHBBQHBBQHBBQH - BEhDQv///////////////////////////////////////////////+vq6g4FAw4FAw4FAw4FAwwFAwwF - AwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgcDAgkDAgcDAgMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////8RBI70/IsJAI8dCJMJAI8dCJMRB - I8VBI70/Ir4/Ir4/Ir0/Irk9Ib4/Irk9Ib0/Ir4/Ir0/Ir0/Ir4/Ir4/IsJAI8JAI8RBI8JAI8RBI8VB - I8VBI8RBI8JAI8lCJMxDJMpDJNFFJdVGJtVGJtlJKdVGJtlIJ9pOLtpOLtpNLdtSM9xVN91aPN1eQd5g - RN9mS99mS99mS+BrUOBrUOBrUOFwV+J1XON7Y+WBauaFb+aHcumTgOqaiOyike2omO2qm++zpe+ypO6u - n+2qm+yklOqZh+iPe+WDbeR8ZeFvVeBoTd1eQdxVN9tUNdlIJ9ZGJs9EJcpDJMRBI74/Ir0/IrY8Ia86 - H6w5H6I2HZkyG5kyG5YyG48wGoUsGH4qF3ImFXAlFGsjE2gjE2MhEl4fEVwfEVoeEFcdEFMcD1UcD1Ab - D1IbD00aDkkYDUsZDkYYDUQWDEYYDUQWDEEWDD8VDEEWDD8VDDoUCzYSCjYSCjUSCjYSCjUSCjMRCTMR - CS4QCTAQCSwPCDAQCS4QCS4QCSsOCCkOCCkOCCcNBykOCCkOCCQMByQMByQMByQMByILBiALBiALBh8L - Bh8LBh0KBhsJBR0KBhsJBRsJBRoJBRoJBRoJBRYHBBYHBBQHBBYHBBYHBBQHBBQHBBMHBL69vf////// - /////////////////////////////////////////4uJiA4FAw4FAw4FAw4FAwwFAwwFAwwFAwoDAgoD - AgoDAgoDAgoDAgoDAgoDAgkDAgkDAgoDAgkDAgkDAgkDAgcDAgcDAgcDAgMBAQMBAQMBAQIBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////8JAI74/IsJAI8RBI8lCJMRBI70/IsJAI8dCJMdCJMA/ - Ir4/IsJAI8JAI8A/IsA/Ir0/Irs9IcJAI74/Ir4/IsA/IsRBI8RBI8RBI8dCJMlCJMxDJMlCJMVBI85E - JdNGJtNGJtlJKdlIJ9ZGJtpLK9pLK9pOLtpOLtpOLtxXOdxVN91eQd5gRN9lSt9kSN9lSuBqT+BsUuBs - UuFvVeJyWeN5YeR8ZeWBauaJdOeLduiSfuqaiOugj+2omO6un++wovC5rPC3qu+zpe+wou2omOyjk+mW - g+eMd+WDbeJ0W+FuVN9mS95iRt1aPNtUNdpOLtlJKc9EJcVBI8RBI8JAI70/IrQ7ILE7IKc3HqA1HZw0 - HJQxG5QxG48wGoMsGHkoFnQnFXImFW8lFGkjE18fEVwfEVwfEVgdEFgdEFUcD1AbD1IbD1AbD00aDkkY - DUcYDUYYDUYYDUQWDEEWDD8VDDwUCzwUCzwUCzgSCjoUCzYSCjUSCjYSCjUSCjMRCTMRCTAQCS4QCS4Q - CTAQCS4QCSwPCCwPCCkOCCcNBysOCCcNByUNByUNByUNByQMByILBiALBh8LBh0KBh0KBh0KBhsJBR0K - BhoJBRoJBRoJBRoJBRgJBRYHBBgJBRYHBBYHBBYHBBQHBBQHBBQHBEdDQf////////////////////// - /////////////////////////+vq6i8qKA8FAw4FAwwFAwwFAwoDAgwFAwoDAgoDAgwFAwwFAwwFAwoD - AgkDAgkDAgkDAgoDAgkDAgcDAgcDAgcDAgcDAgcDAgMBAQMBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////8RBI8A/IsJAI74/Ir0/IsA/Irs9Ib4/IsA/IsA/IsdCJMVBI8RBI8A/IsRB - I70/IsA/Ir4/Ir4/IsA/IsA/IsJAI8JAI8RBI8RBI8lCJMdCJMpDJMlCJM5EJdFFJdFFJdNGJthHJtZG - JthHJtpLK9pOLttSM9pPMNtRMt1aPN1bPt1eQd5gQ99lSt9mS+BsUuFuVOFwV+FvVeJ1XON3XuR8ZeWD - beaHcuiPe+mTgOmXheugj+ymlu6un++zpfC2qfG9sfG8sPC5rPC5rO+wou2omeugj+mWg+eMd+WDbeN5 - YeFwV+BqT99kSN1cP9xXOdpOLtlJKdNGJsxDJMlCJMRBI74/Ir4/Irg9IaI2HaA1HZw0HJYyG40uGYgt - GH4qF3QnFXUnFW8lFGgjE2EhEl8fEV4fEVwfEVcdEFMcD1McD1IbD1AbD04aDk0aDksZDkYYDUQWDEQW - DEIWDEIWDEEWDEIWDDoUCzwUCzoUCzgSCjYSCjMRCTUSCjMRCTMRCS4QCTAQCTAQCTEQCTAQCSsOCC4Q - CSkOCCkOCCkOCCkOCCUNByQMByUNByQMByQMByQMByALBh8LBh0KBh0KBhsJBR0KBhsJBRsJBRsJBRoJ - BRgJBRYHBBgJBRYHBBYHBBYHBBQHBBQHBBMHBBEGA6elpP////////////////////////////////// - /////////////6alpA8FAw8FAw4FAwwFAwoDAgoDAgwFAwoDAgoDAgoDAgoDAgoDAgoDAgkDAgkDAgkD - AgcDAgcDAgcDAgkDAgkDAgcDAgMBAQMBAQIBAQIBAQIBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////74/IsJAI8JAI70/Ir4/IsJAI8JAI8JAI74/Ir0/Ir4/IsJAI8JAI8VBI8lCJMxDJMVBI8VBI8dC - JMA/IsdCJMRBI8A/IsRBI89EJc5EJcdCJMVBI8lCJM9EJdFFJc9EJdVGJtVGJtlIJ9pNLdpOLtpOLtpN - LdpPMN1aPN5gRN5gQ91eQd9kSOBrUOBrUOFvVeFvVeJyWeJ0W+N5YeR/aOaHcuaHcuiPe+mVguuejOyj - k+2omO+wovC2qfG6rvK/s/LBtvLDuPLAtfLBtvC3qu+wouyklOufjumVguaJdOWBauN4YOJyWeBrUN9m - S95iRtxXOdpOLtlIJ89EJcpDJMlCJNVGJtpOLs5EJa05H6M2HZ40HJkyG5szHJEwGosuGX4qF3koFnQn - FWsjE2gjE2MhEmEhEl4fEVgdEFcdEFMcD04aDk4aDk0aDk0aDksZDkkYDUkYDUQWDEIWDEEWDD8VDD8V - DD0UCzwUCzwUCzgSCjgSCjUSCjMRCTMRCTAQCTAQCTMRCTAQCTAQCTAQCSsOCCsOCCkOCCcNBykOCCsO - CCcNByUNByUNByILBiILBiILBiILBiILBh8LBh0KBh0KBh8LBh0KBhsJBRsJBRoJBRgJBRoJBRgJBRYH - BBYHBBYHBBYHBBYHBBMHBBEGAzIrKfX19f////////////////////////////////////////////X1 - 9S8qKA4FAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgkDAgkDAgkDAgoDAgkDAgkDAgkDAgkDAgkDAgcDAgkD - AgkDAgcDAgMBAQIBAQMBAQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8dCJMRBI8dC - JMJAI8RBI74/IsA/IsA/IsRBI8RBI70/Ir4/IsJAI8JAI8JAI8VBI8JAI8RBI8xDJMpDJMVBI8RBI8pD - JMdCJMJAI8lCJMlCJMxDJMxDJM9EJdNGJs9EJdhHJtpLK9hHJtpNLdpLK9pPMNpPMNtRMtxXOd5gRN1e - Qd9lSuBqT+FvVeJyWeFwV+J1XON5YeR8ZeWBauaFb+eLdumWg+mXheufjuyklO6sne+wovC5rPG9sfLB - tvLDuPPFuvPHvfPFuvLDuPG9sfC3qu+wou2omOqciumWg+eMd+WCbON5YeFuVOBsUt9lSt1bPttSM9pO - LtlIJ9FFJdZGJtpOLtxVN9lJKbg9Iac3HqI2HZszHJszHJYyG5EwGoErF3wpFncnFXQnFXAlFGsjE2gj - E18fEVwfEVoeEFcdEFUcD1IbD1AbD04aDk0aDksZDkkYDUQWDEYYDUIWDD8VDD8VDD8VDDoUCzgSCjYS - CjgSCjYSCjUSCjMRCTMRCTUSCjEQCTAQCTAQCS4QCSwPCC4QCSsOCCsOCCcNBysOCCcNByUNByQMByIL - BiQMByILBiILBiALBh8LBiALBiALBh8LBh8LBh0KBhsJBRsJBRoJBRoJBRgJBRgJBRYHBBQHBBMHBBQH - BBMHBBMHBBMHBKalpP///////////////////////////////////////////////6alpA4FAw4FAw4F - AwwFAw4FAw4FAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgMBAQMB - AQIBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////8dCJMVBI8dCJMRBI74/IsRBI74/ - IsJAI8A/IsA/IsRBI8RBI8A/IsJAI74/IsVBI8RBI8pDJMpDJMlCJMlCJM9EJclCJMpDJMlCJM5EJcxD - JM5EJcxDJM5EJc9EJdNGJthHJtpLK9lJKdpOLttRMttUNdtUNdtUNd1bPt9kSN9lSuBqT+FuVOFuVOJ1 - XON4YOR8ZeR/aOWDbeaHcuiPe+mVguqZh+ufjuyklO6sne+zpfG9sfG9sfLDuPPFuvPHvfTKwfXNxPTJ - v/TKwfLDuPG9sfC3qu6un+yklOufjumVgueLduaFb+N7Y+JyWeBrUN5gRN1bPtxVN9lJKdlIJ9ZGJtlJ - KdpNLcxDJLQ7IKo4Hqo4HqI2HZw0HJYyG5EwGoYsGH4qF3cnFXUnFXAlFG8lFGgjE2QhEl8fEV4fEVcd - EFcdEFAbD1AbD00aDk4aDksZDk0aDkcYDUcYDUIWDEEWDEEWDD0UCzoUCzgSCjgSCjgSCjYSCjUSCjUS - CjMRCTMRCTMRCTAQCTAQCS4QCTAQCS4QCSwPCCsOCCkOCCkOCCcNByUNByUNByILBiILBiQMByILBiAL - BiALBh8LBh0KBhsJBRsJBRsJBRoJBRsJBRoJBRgJBRYHBBYHBBQHBBQHBBQHBBQHBBEGAxMHBBMHBBEG - A+vq6v///////////////////////////////////////////////0ZCQQ4FAwwFAwwFAwwFAwwFAwwF - AwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgkDAgcDAgMBAQMBAQIBAQMBAQMBAQIB - AQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////8RBI8RBI8VBI8JAI8JAI8JAI8A/IsRBI8dCJMRBI8RB - I8VBI8JAI8VBI8RBI8JAI8RBI8JAI8VBI8RBI8VBI8VBI85EJdNGJtFFJc9EJcpDJMxDJNVGJtZGJtVG - JtVGJtZGJtlIJ9pNLdtSM9xVN9xVN9xXOd1bPt5gQ99kSOBoTeFuVOJyWeN5YeR9ZuWBauR/aOWCbOeL - duiSfumWg+qciuqaiOyklO2qm++zpfG8sPLAtfPFuvTJv/TKwfXPxvXPx/bUzPXRyfbTy/PHvfTJv/K/ - s/C2qe6un+yklOuejOmTgOeMd+aFb+R8ZeFvVeBoTd5gRNxYOtpPMNlJKdNGJtNGJspDJL0/IrM7IKc3 - Hqg3HqI2HZ40HJszHJQxG40uGYAqF34qF3QnFXUnFXImFWgjE2EhEmQhEl4fEVwfEVcdEFUcD1McD1Ib - D1AbD04aDkkYDU0aDkcYDUYYDUIWDEEWDDoUCzwUCzwUCzoUCzoUCzYSCjMRCTMRCTMRCTAQCTEQCTAQ - CTEQCTAQCSwPCCwPCCsOCCkOCCkOCCcNBykOCCUNByQMByQMByUNByQMByILBiILBiILBh0KBhsJBRsJ - BRoJBRsJBRoJBRsJBRgJBRgJBRgJBRgJBRQHBBQHBBQHBBQHBBMHBBMHBBMHBBEGA4uJiP////////// - /////////////////////////////////////769vQ4FAw4FAwwFAwwFAwoDAgoDAgwFAwoDAgoDAgkD - AgkDAgkDAgkDAgcDAgcDAgkDAgkDAgcDAgkDAgcDAgMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////8dCJMlCJMpDJMpDJL4/IsRBI8RBI70/Ir4/IsRBI74/IsVBI8RBI8VBI8JA - I8VBI8VBI8VBI8dCJMlCJMVBI8dCJMdCJMVBI8xDJNFFJdNGJtNGJtZGJtNGJtFFJdZGJthHJtpLK9pO - LtpPMNxVN9tSM91bPt9lSuBoTeBsUuFuVON3XuJ1XOR8ZeR/aOWBauaHcueNeeiPe+mWg+qciuyklO2o - me6sne+0p/G8sPLBtvPFu/TKwfXRyfbTy/bUzPfX0Pfa0/fZ0vfZ0vbTy/XPx/PFuvK/s/C2qe6uoOym - luqaiOiSfuaHcuWBauN5YeBsUuBqT91eQd1aPNpOLtZGJs5EJcVBI70/Irk9Ia86H6w5H6M2HaI2HaA1 - HZkyG5EwGoUsGH4qF3ooFncnFXQnFWsjE2gjE2QhEl8fEV4fEVcdEFcdEFUcD1IbD1AbD04aDksZDk0a - DkkYDUkYDUQWDEIWDD8VDD0UCz8VDDgSCjgSCjYSCjUSCjYSCjMRCTMRCTEQCTAQCTEQCS4QCS4QCS4Q - CSwPCCsOCCkOCCsOCCkOCCUNBycNByUNByUNByQMByILBiALBh8LBh0KBh0KBh8LBh0KBhoJBRoJBRsJ - BRoJBRgJBRgJBRYHBBYHBBQHBBQHBBYHBBQHBBMHBBMHBBEGAxEGA+vq6v////////////////////// - /////////////////////////0ZCQQ4FAwwFAwwFAwwFAwoDAgoDAgoDAgkDAgkDAgoDAgkDAgkDAgkD - AgkDAgkDAgkDAgkDAgkDAgkDAgMBAQMBAQIBAQMBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////8lCJMpDJMdCJMdCJMlCJMdCJMdCJMdCJMJAI8VBI8dCJMlCJMpDJMRBI8JAI8JAI8VBI8dCJMlC - JMVBI8VBI8pDJMlCJMpDJMxDJMxDJM5EJc5EJdhHJtpOLtlJKdpLK9pOLtpOLttUNdtSM91aPN1cP95g - Q+BoTeBqT+FvVeJ0W+N5YeR9ZuR9ZuWDbeaHcueMd+iRfeqZh+ufjuyklO2qm+6uoO+0p/C5rPLDuPTJ - v/XPx/bUzPfX0Pfa0/jb1fje2Pni3fjg2vjg2vjb1ffX0PXRyfTJv/K/s/C5rO+wou2omOqciumTgOaJ - dOWCbON4YOFuVN9mS95gQ9tSM9pLK9NGJspDJMVBI7s9IbQ7IK86H6o4Hqc3HqI2HZszHJIwGoouGX4q - F3ooFnkoFnAlFG8lFGgjE2MhEl4fEV8fEV4fEVgdEFUcD1UcD1UcD1IbD1AbD04aDksZDkYYDUcYDUYY - DUEWDEEWDDwUCz0UCzoUCzgSCjUSCjYSCjUSCjMRCTMRCTEQCTAQCS4QCTAQCS4QCS4QCSsOCCsOCCsO - CCkOCCkOCCUNByUNByUNByUNByQMByILBiALBh8LBiALBh8LBhsJBRsJBRsJBRsJBRsJBRgJBRgJBRYH - BBYHBBYHBBQHBBYHBBMHBBMHBBMHBBMHBBEGA2xpaP////////////////////////////////////// - /////////9XU1A4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkD - AgkDAgkDAgMBAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8dCJMdCJMRB - I8pDJMpDJMRBI8VBI8dCJMxDJM5EJcpDJM5EJclCJMJAI8dCJMVBI8pDJMVBI8pDJMVBI8lCJMlCJMVB - I85EJdFFJcxDJMpDJNNGJtVGJtFFJdlJKdpLK9pPMNxXOd1eQd1eQd1eQd5gRN9lSt9kSOBsUuFvVeR8 - ZeN7Y+R8ZeWDbeaHceaJdOiSfuqaiOqciuyklO2qm++ypPC5rPG8sPLBtvTLwvXPx/bUzPfa0/jd1/jg - 2vnh3Pni3frn4/nk3/rm4fjg2vjg2vfX0PXRyfTKwfLBtvC5rO6un+yklOufjumTgOaHcuWBauJ1XOBs - Ut9lSt1cP9tRMtpLK9NGJspDJMA/Ir0/Irg9Iaw5H6c3HqA1HZ40HJYyG4suGYUsGH4qF3koFnUnFXAl - FGkjE2gjE2MhElwfEVwfEVcdEFUcD1IbD1McD04aDlIbD04aDksZDksZDkkYDUYYDUQWDEQWDEIWDD0U - CzYSCjgSCjgSCjYSCjYSCjEQCTEQCTEQCTEQCTEQCTAQCS4QCSwPCCsOCCsOCCwPCCcNBykOCCcNByQM - ByQMByALBiALBiALBiALBiALBiALBh0KBh0KBhsJBRoJBRoJBR0KBh0KBhgJBRYHBBYHBBYHBBYHBBQH - BBMHBBMHBBEGAxEGAxMHBBEGA9XU1P///////////////////////////////////////////////2tp - aAwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgcDAgUCAQMB - AQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////8lCJMlCJMdCJMlCJMxDJMpDJMVB - I8VBI8VBI8pDJMdCJMlCJMlCJMpDJMdCJMlCJMlCJM5EJcxDJMlCJM9EJcxDJM5EJcpDJNNGJs5EJcxD - JNFFJdhHJtlIJ9pNLdpOLtpPMNxYOt1cP99kSOBoTeBrUOBrUOBrUOFvVeJyWeN3XuR9ZuaHceaHcueM - d+iRfemVguqciuymlu2qm+6uoPG6rvG8sPPFuvTLwvbTy/fX0Pjd1/jg2vrm4frm4fvq5vro5Pvs6Pvs - 6Pvs6Prn4/rn4/ni3fjb1fbVzvTLwvLDuPG6ru6un+yklOqaiOiSfuaHcuR9ZuJyWeBoTd5gQ9xXOdpP - MNhHJs5EJcVBI70/Irk9Ia86H6g3HqI2HaA1HZkyG5QxG4YsGIAqF3wpFnkoFnQnFWkjE2YhEmYhEl8f - EVoeEFgdEFcdEFIbD1McD1McD1AbD1IbD1AbD0sZDkkYDUYYDUQWDEIWDEEWDEEWDD0UCzoUCzgSCjYS - CjYSCjUSCjEQCTEQCTAQCTAQCTEQCSwPCCwPCCsOCCsOCCsOCCkOCCkOCCcNByUNByQMByILBiALBiIL - BiALBiALBh0KBh8LBhsJBRsJBRsJBRsJBRoJBRoJBRYHBBYHBBYHBBQHBBYHBBQHBBMHBBEGAxMHBBMH - BBMHBBEGA2xpaP///////////////////////////////////////////////9XU1AwFAwwFAwwFAwwF - AwoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgoDAgkDAgkDAgkDAgkDAgkDAgUCAQMBAQMBAQMBAQMBAQIB - AQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////8lCJMpDJMxDJMpDJMlCJMdCJMVBI8VBI8dCJMVBI8VB - I8lCJMVBI8lCJMxDJMdCJMxDJMxDJM9EJdZGJs5EJc5EJdFFJdVGJs5EJc5EJdFFJdNGJthHJtlIJ9pL - K9pPMNtSM91aPN1eQd5gRN9lSuFvVeJ0W+N3XuJ0W+R8ZeR/aOWDbeeMd+eMd+iSfumXheuejOyklO2o - me+ypPC5rPLAtfPFuvXPxvXPx/fa0/je2Pnh3Prm4fvs6Pvs6fvs6fvu6/vu6/vu6/vu6/vs6fvs6fvq - 5vnk3/je2PfX0PXNxPLDuPC3qu6un+yikeqciuiPe+WDbeN5YeFvVd9lSt5gRNxVN9pOLthHJsxDJMA/ - Irk9IbQ7IKw5H6c3HqI2HZ40HJcyG40uGXwpFnkoFnUnFXImFW8lFGsjE2YhEl8fEWEhElcdEFgdEFcd - EFMcD1IbD04aDk0aDksZDkkYDUcYDUcYDUYYDUIWDD8VDD8VDEEWDDwUCzwUCzYSCjYSCjYSCjMRCTMR - CTEQCS4QCTAQCS4QCTAQCS4QCS4QCSkOCCkOCCcNBycNByUNByUNByQMByALBiQMByALBiILBiILBh8L - Bh0KBhsJBR0KBhsJBRsJBRgJBRYHBBYHBBYHBBQHBBYHBBYHBBQHBBEGAxEGAxMHBBMHBBEGAxEGA8rJ - yf///////////////////////////////////////////////4qJiA4FAwwFAwwFAwwFAwwFAwoDAgkD - AgkDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgUCAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////8pDJMlCJMxDJMlCJMlCJMlCJMRBI8VBI8dCJMdCJMlCJMJAI8VBI8dCJMVB - I8JAI8pDJMxDJM9EJcdCJMpDJM9EJc9EJdFFJdVGJtVGJthHJtpNLdpOLtpOLttSM9pPMNxVN91aPN5g - RN5gRN9kSOBsUuFuVOJyWeN7Y+WDbeaJdOeLduiPe+mTgOmWg+qciuyklO2omO+wovC3qvK/s/PFuvXP - xvbTy/fa0/jg2vnk3/ro5Pvs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Prn4/jg2vfZ - 0vTKwfLBtvC2qe6sneyikemWg+eMd+WBauN3XuBsUt9mS95gQ9tUNdpNLdVGJslCJMRBI7s9IbM7IK86 - H6g3HqA1HZYyG4gtGH4qF3ooFnUnFW8lFG8lFG0kFGQhEl8fEVwfEVwfEVgdEFcdEFMcD1IbD1IbD1Ib - D00aDksZDk4aDksZDkYYDUQWDEQWDEEWDD8VDD0UCzoUCzYSCjgSCjUSCjMRCTMRCTMRCTMRCS4QCTAQ - CTAQCSwPCCsOCCsOCCkOCCcNBysOCCcNBycNByQMByILBiQMByALBiALBiALBiALBh0KBhsJBR0KBhoJ - BRsJBRgJBRgJBRgJBRYHBBYHBBQHBBYHBBQHBBMHBBMHBBMHBBMHBBEGAxEGA0dDQf////////////// - /////////////////////////////////+vq6g4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgkDAgkDAgkD - AgkDAgkDAgkDAgkDAgcDAgkDAgUCAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////8pDJMpDJMlCJMlCJMlCJMlCJMdCJMpDJMpDJMRBI8JAI8dCJMlCJMpDJMlCJMdCJMlCJMRBI8pD - JM5EJcxDJMlCJM9EJdFFJdFFJdVGJtlIJ9pOLtpOLttSM9tUNdtUNd5gQ91eQd5gQ+BqT+BsUuFvVeFv - VeN3XuWBauaHceiPe+mTgOqZh+uejOuejOyklO2qm++wovC5rPK/s/LDuPTKwfbUzPfa0/jg2vnk3/rn - 4/vs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6frn4/jg2vbTy/TJv/LAte+z - pe2qm+uejOmWg+eLduaFb+N3XuBrUN9lSt1aPNtRMtlJKc9EJcpDJL4/Irk9Ia86H6g3Hpw0HJcyG48w - GoUsGHwpFnooFncnFXQnFXAlFGkjE2EhEl4fEV8fEVgdEFcdEFcdEFUcD1McD1IbD1IbD0sZDk0aDksZ - DkcYDUYYDUQWDEEWDEIWDDwUCzwUCzwUCzoUCzgSCjYSCjUSCjEQCTEQCS4QCS4QCSwPCCwPCCwPCCkO - CCcNBykOCCkOCCUNByUNByQMByQMByQMByALBh8LBiALBh8LBh8LBh0KBh0KBhsJBRoJBRgJBRgJBRYH - BBYHBBQHBBQHBBQHBBQHBBMHBBMHBBMHBBMHBBEGAxEGAxEGA769vf////////////////////////// - /////////////////////4qJiAwFAwoDAgoDAgwFAwoDAgoDAgoDAgkDAgoDAgkDAgkDAgkDAgkDAgkD - AgkDAgkDAgUCAQUCAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8xDJMpDJM5E - JcpDJNFFJcdCJMlCJMdCJMpDJMRBI8lCJMlCJMdCJMdCJMlCJMVBI8lCJM9EJcdCJM5EJc9EJc5EJdFF - JdVGJtZGJtlIJ9lJKdpOLtpOLttSM9xVN91eQd9kSOBoTeBrUOFwV+R8ZeR8ZeN5YeWBauaHcueMd+iS - fumVguqciuyikeymlu+wovC2qfC5rPLAtfLDuPTLwvXPx/fZ0vje2Pnh3Pro5Pvs6fvu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6frm4fje2PXRyfPHvfG9se+0p+2qm+ugj+mT - gOaJdOR9ZuJ1XOFvVd9mS91bPttRMtpLK9FFJclCJL0/Irk9IbM7IKg3HqM2HZszHIYsGHwpFn4qF3cn - FXImFXAlFGkjE2kjE2EhEmEhEl4fEVoeEFwfEVoeEFgdEFcdEFUcD1IbD04aDksZDkYYDUQWDEYYDUIW - DD8VDD8VDDoUCzwUCzwUCzgSCjoUCzUSCjMRCTMRCTAQCS4QCTAQCS4QCSwPCC4QCSsOCCsOCCkOCCUN - ByUNByILBiQMByQMByALBiALBiILBiALBh0KBh8LBh8LBh0KBhoJBRsJBRoJBRgJBRYHBBYHBBQHBBYH - BBMHBBMHBBQHBBMHBBEGAxEGAxEGAxEGAzAqKPX19f////////////////////////////////////// - //////X19S4qKAwFAwoDAgwFAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgkDAgcDAgcDAgcDAgkDAgUCAQMB - AQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9NGJslCJM5EJcxDJMlCJMxDJM5E - JclCJMdCJMpDJMVBI8JAI8dCJMpDJMpDJMpDJM5EJcxDJMlCJM5EJcxDJM9EJdFFJdFFJdZGJtlIJ9pN - LdpNLdtSM9xYOtxYOt9kSOBrUOBrUON3XuN3XuWCbOaHceaHcuaJdOmVguqZh+qaiOufjuyklOyklO6u - n++ypPC5rPK/s/LDuPTLwvXRyfbVzvje2Pjg2vrn4/vs6Pvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Pni3ffa0/XPxvLDuPG6ru6uoO2omOuejOmVgueLduWCbON7 - Y+FwV+BoTd5gQ9xYOtpOLtlIJ89EJcdCJL0/IrM7IK05H6A1HZYyG4suGYUsGHwpFnooFncnFXImFWsj - E2gjE2YhEmQhEmMhEl8fEVwfEVgdEFgdEFcdEFUcD1McD00aDkcYDUQWDEIWDEIWDEEWDD8VDDwUCzoU - CzwUCzoUCzYSCjYSCjMRCTUSCjMRCTEQCTAQCTAQCS4QCSwPCCsOCCwPCCwPCCcNByUNByQMByQMByQM - ByQMByALBiALBh8LBh8LBh0KBh0KBh0KBhsJBRgJBRgJBRgJBRYHBBYHBBYHBBQHBBMHBBQHBBQHBBMH - BBMHBBMHBBEGAxEGAxEGA6alpP///////////////////////////////////////////////6alpAwF - AwoDAgoDAgoDAgoDAgoDAgoDAgkDAgoDAgoDAgkDAgcDAgkDAgcDAgcDAgUCAQMBAQMBAQMBAQIBAQMB - AQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////8xDJM9EJcxDJM9EJclCJMpDJMlCJMxDJMxDJMpDJMpD - JMdCJMdCJM5EJclCJMxDJM5EJdFFJcxDJM5EJc5EJdVGJtFFJdlIJ9lJKdpNLdpPMNtSM95gRN5iRt9l - SuBrUOFwV+N7Y+WCbOaFb+eNeeiPe+iRfeiSfuqciuuejOyjk+2omO6sne6un++ypPC3qvG8sPLDuPPF - u/XNxPbTy/fa0/jg2vni3fvs6Pvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vs6frn4/jg2vbVzvTJv/LAtfC3qu6uoOymluqciumVgueLduWCbON7Y+JyWeBrUN9l - St1aPNxXOdpOLtlIJ85EJcRBI8JAI7k9Iaw5H6M2HZYyG5EwGogtGHwpFnooFnImFWsjE2YhEmMhEl4f - EVoeEFoeEFgdEFcdEFUcD1UcD1IbD0sZDkkYDUcYDUYYDUIWDD8VDDwUCzwUCzwUCzwUCzgSCjgSCjUS - CjMRCTUSCjUSCjMRCTMRCTEQCTAQCSwPCCsOCCkOCCkOCCkOCCkOCCcNByUNByQMByILBiILBiILBh8L - Bh0KBh0KBh0KBh8LBh0KBhsJBRsJBRsJBRgJBRgJBRYHBBMHBBQHBBQHBBQHBBEGAxMHBBMHBBEGAxEG - AxEGAzAqKPX19f////////////////////////////////////////////X19S4qKAoDAgwFAwoDAgoD - AgoDAgoDAgkDAgoDAgkDAgkDAgcDAgcDAgcDAgcDAgUCAQUCAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////8xDJM5EJcpDJMlCJMpDJMxDJNFFJcpDJMxDJM5EJc9EJc5EJclCJMpDJMpD - JMxDJNFFJcpDJNFFJdVGJtFFJdVGJs9EJdlJKdpOLtxXOd1cP99mS+BrUOFwV+N5YeR/aOR/aOaFb+eL - duiRfemXheqaiOmWg+qaiOugj+ymluymlu2qm+6uoO+zpfC2qfG9sfLBtvPFu/TJv/bTy/bUzPjb1fjg - 2vrn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vs6Pni3fjb1fXPx/PFu/K/s++0p+6sneyklOufjumVgueMd+aHcuWCbOR9ZuJyWeJyWeBqT99mS95g - RNxXOdpPMNpNLdNGJsA/IrQ7IKc3HpszHJEwGoMsGHcnFXQnFWsjE2QhEmEhEmEhEloeEFgdEFcdEFUc - D1McD1McD04aDk0aDkkYDUkYDUQWDEQWDEIWDD0UCz8VDDoUCzwUCzoUCzoUCzUSCjUSCjUSCjMRCTMR - CTMRCTEQCTEQCTAQCSsOCCsOCCcNBycNBycNBycNBycNByQMByILBiILBiALBiALBh8LBh0KBh0KBh8L - BhsJBRsJBRgJBRoJBRYHBBgJBRYHBBQHBBMHBBQHBBEGAxEGAxMHBBMHBBEGAxEGAxEGAxEGA5mXl/// - /////////////////////////////////////////////769vQoDAgwFAwoDAgoDAgoDAgoDAgkDAgkD - AgkDAgkDAgkDAgkDAgcDAgcDAgMBAQUCAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////89EJdFFJc9EJc9EJc5EJc5EJc5EJcxDJMxDJM5EJc5EJclCJMlCJMpDJMpDJMlCJM9EJc5EJcxD - JNFFJc9EJdZGJtlIJ9pOLtxYOuBoTeFwV+N7Y+WBauWCbOaHcuaJdOeMd+iRfemWg+qZh+uejOugj+uf - juyikeymlu2omO6sne6uoO+zpfC2qfG6rvK/s/LDuPPHvfTLwvXPx/bVzvjd1/nh3Prn4/vs6Pvu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fnk3/je2PbU - zPTKwfLDuPG8sO+0p+2qm+ymluugj+qZh+mVguiSfueLduaFb+R9ZuN5YeJ0W+FuVN9lSt5gQ9xXOdpP - MNZGJslCJL4/IrQ7IKI2HZQxG34qF3QnFWgjE2MhEl8fEV8fEVoeEFcdEFcdEFcdEFcdEFAbD04aDksZ - DksZDkYYDUQWDEIWDEYYDUEWDD0UCz8VDD0UCzoUCzwUCzYSCjMRCTMRCTMRCTMRCTMRCTEQCTMRCTAQ - CS4QCS4QCSsOCCcNBycNBycNBycNByILBiALBiILBiILBiALBh8LBh8LBh8LBh0KBhsJBRsJBRoJBRoJ - BRgJBRQHBBQHBBMHBBQHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGA+vq6v////////////// - /////////////////////////////////0VBQAwFAwwFAwoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkD - AgkDAgkDAgMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////89EJc9EJc5E - JdNGJs5EJdFFJdFFJdNGJsxDJM5EJclCJMRBI8xDJMpDJNVGJs9EJdZGJs9EJcxDJM9EJdVGJtlJKdpO - Lt5gQ+J1XOWCbOaFb+aJdOeNeeeNeeiSfumTgOqZh+qZh+qciuuejOyikeugj+ymlu2qm+2qm+6un++w - ou+ypO+0p/C3qvG8sPLAtfLDuPPFu/XNxPbTy/fX0Pjb1fnh3Prm4fvs6Pvu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/ro5Pjg2vfa0/XRyfTJv/LDuPG8 - sO+zpe6sneymluufjuqZh+mTgOeNeeaHcuaFb+R/aON7Y+N3XuJyWeBrUOBoTd9kSN1aPNpPMNpLK9VG - JsVBI7g9IZ40HIouGXUnFWgjE2EhEmEhEl8fEVwfEVoeEFcdEFgdEFMcD04aDksZDkcYDUYYDUYYDUQW - DEYYDT8VDD0UCz8VDDwUCzoUCzoUCzgSCjUSCjUSCjMRCTMRCTMRCTEQCTMRCTAQCTAQCS4QCS4QCSsO - CCcNByUNByUNByILBiALBiILBiILBiALBh8LBh0KBh0KBh0KBhsJBRsJBRoJBRoJBRYHBBQHBBYHBBQH - BBQHBBYHBBQHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGA4uJiP////////////////////////////// - /////////////////769vQwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgUCAQMB - AQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////89EJc9EJdVGJs5EJc5EJc9EJcxD - JMpDJM9EJclCJMpDJMlCJM5EJdVGJs9EJc9EJdNGJs5EJdFFJdFFJdVGJtNGJtxYOuFuVOWBauaHcuiP - e+iRfeiSfumVgumWg+mWg+mXheufjuqaiOufjuyjk+yjk+yklO2ome2qm+6un+6uoO+zpe+0p/C5rPG9 - sfLAtfLDuPTJv/TLwvXRyfbVzvjd1/nh3Pro5Pvs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/ro5Pnh3Pjb1fXRyfTLwvLDuPG9se+0p+6uoO2omeyk - lOufjuqZh+mTgOiRfeeMd+aHcuaHceWDbeR9ZuN7Y+J1XOJyWeBrUN9mS99kSN5gQ9xYOtpOLs5EJbg9 - IYouGXAlFGYhEmEhEl8fEVoeEFcdEFcdEFMcD1IbD04aDksZDkcYDUYYDUQWDEQWDEEWDEEWDD0UCzwU - Cz0UCzoUCzgSCjgSCjgSCjUSCjMRCTMRCTMRCTAQCTMRCTAQCTAQCTAQCS4QCSwPCCsOCCcNByUNByQM - ByILBiQMByQMByILBh8LBiALBh0KBh0KBhsJBRsJBRoJBRoJBRQHBBYHBBQHBBYHBBQHBBMHBBMHBBEG - AxMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGA9XU1P////////////////////////////////////////// - /////2poaAoDAgoDAgoDAgkDAgoDAgkDAgkDAgoDAgkDAgkDAgkDAgkDAgMBAQMBAQMBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9NGJtNGJtFFJc5EJdFFJcxDJNVGJtVGJsxDJM9EJcxD - JNNGJtNGJs9EJc5EJdNGJs5EJdZGJspDJNVGJtVGJtVGJttRMt1cP+FwV+WBauaHcuaJdOaJdOeNeemT - gOmTgOmVgumXheqciuqciuufjuyikeyklOyklO2ome2qm+6sne+wou+0p/C3qvG8sPLAtfLDuPPFuvTK - wfXPx/fZ0vjd1/jg2vrm4fvs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vs6fro5Pjg2vfX0PXPxvPFu/LAtfG6ru+ypO6un+2omOyklOuejOqZh+mTgOiR - feeLduaHcuaHceWCbOR9ZuN7Y+N4YOJ0W+FuVOBrUOBqT99lSt5gRN1bPtpOLsVBI5w0HHImFWEhElwf - EVgdEFgdEFcdEFUcD1IbD04aDk4aDksZDksZDkcYDUcYDUYYDUQWDEEWDDwUCz8VDDwUCzwUCzoUCzgS - CjUSCjgSCjYSCjMRCTMRCTEQCTEQCTEQCS4QCS4QCSwPCCwPCCwPCCsOCCcNByUNByILBiQMBx8LBh8L - Bh8LBh8LBh0KBhsJBRsJBRoJBRoJBRsJBRoJBRYHBBQHBBYHBBYHBBQHBBQHBBEGAxMHBBMHBBEGAxEG - AxEGAxEGAxEGAw8FA2xpaP///////////////////////////////////////////////9XU1AoDAgoD - AgoDAgkDAgoDAgoDAgkDAgoDAgkDAgkDAgcDAgcDAgMBAQMBAQMBAQMBAQIBAQMBAQIBAQMBAQMBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9FFJdNGJtVGJs5EJdNGJs9EJdNGJsxDJM9EJdVGJs5EJc5EJdVGJs5EJdFF - JcxDJNFFJdFFJc5EJdVGJthHJtlIJ9ZGJtpPMN1bPuBoTeFuVON5YeR8ZeR9ZuaHceeLdueMd+iRfemX - hemXheuejOqaiOugj+ufjuyklOymlu2omO6sne+ypO+zpfC2qfG9sfLAtfPFuvTJv/XPxvbVzvjb1fnh - 3Prn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vs6frm4fje2PbVzvTKwfLDuPK/s++0p+6uoOymluyjk+qciuqaiOiSfueMd+aJdOaFb+WDbeR/aON7 - Y+N5YeJyWeFuVOBrUN9kSN1eQd1cP9xXOdtRMthHJsdCJKw5H4ErF2gjE14fEVoeEFgdEFcdEFUcD1Ib - D1AbD04aDk4aDk4aDkkYDUcYDUcYDUQWDEQWDEEWDD8VDD8VDD8VDDwUCzoUCzoUCzYSCjYSCjYSCjUS - CjMRCTMRCTMRCTEQCTAQCTAQCSwPCC4QCSsOCCsOCCkOCCkOCCUNByQMBx8LBh8LBh0KBh8LBh0KBhsJ - BRsJBRsJBRoJBRoJBRgJBRYHBBYHBBYHBBYHBBQHBBQHBBMHBBQHBBMHBBEGAxEGAxEGAxEGAxEGAw8F - Aw4FA9XU1P///////////////////////////////////////////////2poaAoDAgoDAgoDAgoDAgkD - AgkDAgkDAgkDAgcDAgcDAgcDAgMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJtVGJtVGJtNGJtFFJc9EJdZGJs9EJdVGJs9EJdVGJtFFJdNGJslCJNFFJcxDJNZGJthHJtFF - JdVGJthHJtlIJ9VGJtpLK9pNLdxVN91bPt5iRuBrUOJyWeN5YeN5YeWCbOWCbOaJdOeMd+iRfeiRfemW - g+qZh+ufjuugj+yklO2ome2qm++wovC2qfC3qvG8sPLDuPPFu/XPxvbUzPfa0/je2Pnk3/vq5vvs6fvu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Prm4fjd1/bU - zPTLwvLDuPG9se+0p+6un+yklOugj+qZh+iRfeeNeeaHcuWCbOWBauN7Y+J1XOFwV+FuVN9mS95iRt1c - P9xXOdpPMNhHJspDJL4/Iq86H5kyG4AqF2kjE2MhEl4fEVoeEFcdEFcdEFMcD1McD1McD00aDk0aDk0a - DksZDkcYDUEWDEIWDD8VDD0UCz8VDD0UCzoUCz0UCzgSCjoUCzgSCjgSCjMRCTMRCTMRCTMRCTEQCTEQ - CTMRCTAQCSwPCCwPCCsOCCwPCCkOCCcNByUNByUNByQMByILBh8LBh0KBh8LBh0KBhsJBRsJBRsJBRoJ - BRgJBRYHBBYHBBQHBBYHBBQHBBMHBBMHBBQHBBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FA1pXVv////// - /////////////////////////////////////////+rq6goDAgoDAgoDAgkDAgoDAgoDAgkDAgkDAgkD - AgcDAgcDAgMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9VGJtZGJtVG - JtNGJtFFJdFFJdFFJdFFJdFFJdZGJtNGJs9EJdFFJc9EJc9EJdNGJtZGJtFFJdZGJtVGJthHJthHJtFF - JdZGJtZGJtlIJ9pNLdtRMt1bPt1bPt9lSuBrUON3XuN4YON4YOaFb+aFb+eLdueLduiSfumVguqaiOue - jOyjk+yklO6un+6un++0p/C5rPG8sPLDuPPHvfXPx/bVzvjb1fnh3Pvq5vvs6Pvu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fro5Pjg2vfZ0vXPx/PFu/G9sfC3qu6u - oO2omeyjk+qZh+mTgOeNeeaHceR/aON7Y+N3XuFwV+BrUN9kSN5gQ9xXOdpOLtlJKc5EJcVBI7s9Iaw5 - H5cyG4suGXwpFnAlFGQhEmEhElwfEVcdEFcdEFUcD1UcD1McD1AbD1AbD04aDk4aDk0aDkcYDUIWDEIW - DEEWDEEWDD8VDDoUCzoUCz0UCzoUCzoUCzYSCjUSCjMRCTUSCjEQCTMRCTMRCTEQCTEQCTAQCS4QCTEQ - CSwPCCsOCCkOCCcNBycNBykOCCUNByILBiALBh8LBh8LBhsJBRoJBRsJBRsJBRgJBRoJBRoJBRgJBRYH - BBQHBBQHBBMHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FA769vf////////////////// - /////////////////////////////4qJiAoDAgoDAgkDAgkDAgkDAgkDAgcDAgcDAgcDAgcDAgMBAQMB - AQMBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////89EJc5EJdVGJtFFJdNGJtVGJthH - JtVGJtZGJtZGJtNGJtVGJtZGJtFFJcxDJMxDJM9EJdZGJtVGJtVGJthHJtVGJtZGJtFFJdVGJtlIJ9lJ - KdpOLtpOLttSM9xXOd1eQd9mS+BrUOBsUuJ1XON7Y+N7Y+R/aOaHcuaJdOeNeeiSfumWg+uejOugj+ym - lu2qm++ypPG6rvG8sPLDuPTKwfbUzPfZ0vjg2vnk3/vq5vvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fvs6Pnk3/jd1/bVzvTKwfLBtvG6ru+zpe2qm+ugj+qZh+iRfeaJ - dOaFb+R/aON5YeFvVeBqT95iRt1cP9xVN9pPMNZGJslCJMA/IrY8Iaw5H6A1HZIwGogtGHkoFnAlFGsj - E2YhEmEhEloeEFoeEFcdEFcdEFUcD1IbD1McD1AbD1IbD00aDk0aDkcYDUcYDUYYDUQWDEEWDD8VDD0U - CzwUCzoUCzgSCjwUCzYSCjYSCjUSCjYSCjMRCTEQCTEQCTEQCTEQCTEQCTAQCSwPCCwPCCwPCCwPCCsO - CCkOCCcNByUNByQMByQMByALBh8LBh0KBhoJBRoJBRsJBRoJBRoJBRoJBRgJBRYHBBQHBBQHBBQHBBQH - BBMHBBMHBBEGAxMHBBMHBBEGAxEGAxEGAxEGAw8FA0dCQf////////////////////////////////// - /////////////+vq6goDAgkDAgoDAgkDAgoDAgkDAgcDAgcDAgcDAgcDAgMBAQIBAQMBAQIBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9ZGJtVGJtVGJtFFJdZGJtNGJthHJtZGJthHJtZGJtVG - Js9EJdVGJs9EJc5EJc9EJc5EJc5EJdNGJtZGJtFFJdNGJtNGJtVGJtlJKdhHJtNGJtlJKdlJKdpOLttU - NdtSM91aPN1eQd9mS+BoTeFvVeJ0W+J1XOR9ZuWBauaHceeNeeiPe+mVgumWg+ugj+ymlu2omO+wovG6 - rvK/s/PFu/TKwfXRyffa0/jg2vrn4/vs6Pvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vs6Prm4fjg2vbVzvTKwfLDuPC5rO6uoO2omOyikeqZh+iSfuaHcuWCbON5YeFuVOBoTd5g - Q91aPNxVN9pOLtlJKc9EJcRBI7Y8Ia05H5w0HJYyG4suGYAqF3wpFm8lFGsjE2YhEmMhEmMhElwfEVgd - EFcdEFcdEFUcD1IbD1McD04aDlAbD0kYDUsZDkcYDUcYDUIWDEIWDEIWDEEWDD8VDDwUCzwUCzoUCzoU - CzYSCjYSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTAQCSwPCC4QCS4QCSsOCCkOCCkOCCkOCCUNByUNByUN - ByUNByQMBx8LBh8LBh8LBh0KBhsJBRsJBRoJBRgJBRYHBBQHBBQHBBYHBBMHBBEGAxMHBBEGAxEGAxMH - BBEGAxEGAxEGAw8FAw8FAw8FAw8FA7KxsP////////////////////////////////////////////// - /6WkpAkDAgoDAgkDAgoDAgkDAgkDAgkDAgkDAgUCAQIBAQIBAQIBAQIBAQMBAQIBAQIBAQMBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9NGJtZGJtlIJ9ZGJthHJthHJthHJthHJtZGJtVGJs5EJdFFJdVGJtNGJtFF - JcxDJNFFJdNGJtNGJs9EJc9EJdVGJs9EJc9EJdZGJtZGJtVGJtVGJtpOLtpOLtlIJ9tSM9tUNdxVN91e - Qd9kSOBqT+BqT+BsUuFwV+J0W+R8ZeaFb+aHcueLdumVgumWg+ufjuyklO2omO6uoO+0p/G9sfPFuvTL - wvbTy/jb1frm4fvq5vvs6fvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Prn4/jg - 2vfZ0vXNxPLDuPG6ru+ypO2omOuejOmWg+iPe+aHcuR9ZuJ1XOFvVd9lSt1eQdtRMtpLK9VGJs5EJcRB - I7s9Ia05H6M2HaM2HZcyG48wGoYsGHooFnUnFXAlFGkjE2YhEmMhEmEhEloeEFgdEFcdEFcdEFMcD1Mc - D1AbD1IbD1AbD0sZDksZDkkYDUcYDUYYDUYYDUEWDD0UCz8VDD0UCzoUCzoUCzgSCjgSCjYSCjgSCjUS - CjUSCjMRCTMRCTMRCTEQCTEQCTAQCTAQCSsOCCkOCCkOCCsOCCkOCCkOCCcNBycNByUNByQMByALBh8L - Bh0KBhsJBRoJBRgJBRYHBBgJBRgJBRYHBBQHBBYHBBQHBBQHBBQHBBMHBBEGAxMHBBEGAxEGAxEGAw8F - Aw8FAxEGAxEGAy8qKPX19f////////////////////////////////////////////X19SwoJwkDAgkD - AgkDAgcDAgkDAgcDAgcDAgUCAQMBAQMBAQMBAQMBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJtVGJtZGJtZGJtlIJ9lIJ9VGJtVGJtVGJtVGJs9EJc9EJdVGJtNGJs9EJcpDJM5EJdNGJs9E - JdZGJtlIJ9ZGJtVGJtlJKdlJKdlIJ9lIJ9VGJtpLK9pLK9pNLdpPMNtRMtxVN9xYOt1bPt5gRN9lSuBo - TeBsUuFwV+J0W+R/aOWDbeaHcuaJdOiPe+mVguqciuugj+yklO6un++0p/G8sPLDuPTLwvfX0Pnh3Pro - 5Pvq5vvs6Pvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Pnk3/jg2vfa0/XPxvPFu/G9se+z - peymluufjumVgueMd+WCbON5YeJyWeBoTd5iRtxYOttRMtlJKdNGJspDJMJAI7g9Ia05H6U3Hp40HJYy - G4UsGIUsGH4qF3koFnkoFnQnFW0kFGkjE2gjE2MhEl8fEVwfEVcdEFUcD1UcD1McD1IbD00aDk4aDksZ - DkkYDUkYDUQWDEQWDEYYDUEWDD0UCz8VDEEWDDwUCzgSCjgSCjgSCjYSCjYSCjUSCjUSCjMRCTEQCTEQ - CTAQCTEQCTAQCS4QCSwPCCwPCCsOCCkOCCkOCCcNBykOCCcNByUNByQMByILBiALBh8LBhsJBRoJBRoJ - BRoJBRgJBRYHBBYHBBQHBBMHBBMHBBQHBBQHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8F - A6alpP///////////////////////////////////////////////6WkpAkDAgkDAgkDAgcDAgkDAgkD - AgcDAgUCAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9hHJthHJtlJ - KdVGJtlIJ9ZGJtVGJtZGJtVGJtNGJtNGJs5EJdNGJtNGJs5EJc9EJc9EJdFFJdVGJthHJtlIJ9ZGJtlJ - KdhHJtZGJtlJKdlIJ9hHJthHJtlJKdpOLtpOLtpOLttUNdxVN9xXOdxYOt5iRt5iRuBoTeFwV+FwV+N3 - XuN4YOR/aOaFb+aHcueLdumVguuejOyikeyklO6sne+wovG8sPPFuvXPxvfa0/nh3Pni3frm4fro5Pvs - 6Pvs6fvs6fvs6fvu6/vu6/vs6fvs6Pro5Pnk3/jg2vfZ0vXNxPPFuvG6ru+ypO2omeuejOmTgOeLduWB - auN4YOFuVOBoTd1cP9xVN9pPMNhHJs5EJclCJMJAI70/Iq86H6c3HpkyG5kyG4suGYMsGIAqF34qF3Qn - FXQnFXAlFGYhEmkjE2QhEmgjE2QhEl4fEVwfEVcdEFMcD1UcD1AbD04aDlAbD00aDksZDkcYDUYYDUYY - DUYYDUIWDD8VDDwUCzwUCzoUCzgSCjgSCjoUCzgSCjgSCjYSCjUSCjMRCTMRCTEQCTEQCTAQCTAQCTAQ - CSwPCCsOCCkOCCkOCCcNBycNBykOCCcNByQMByALBiILBiQMByALBh0KBk1FQ6ilpailpailpaelpKel - pKelpKelpKelpKelpKelpKelpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpLKxsP////////// - /////////////////////////////////////////0VBQAkDAgkDAgkDAgkDAgkDAgcDAgMBAQMBAQIB - AQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9ZGJthHJtpLK9VGJtVGJtVGJthH - JtVGJtVGJs5EJc9EJdFFJdNGJs9EJc5EJc9EJcxDJNZGJthHJthHJthHJthHJtVGJtNGJtZGJthHJtVG - JtNGJtlIJ9hHJtpNLdpLK9pOLtpPMNtUNd1aPNxYOt9kSN5gRN5iRt9mS+BrUOFvVeFvVeN5YeN5YeR9 - ZuaJdOeLduiRfemXheqZh+yklO6un/C5rPLBtvPFu/XPxvbVzvfZ0vje2Pnh3Pni3frm4fro5Pro5Pvq - 5vro5Pvq5vrm4fjg2vjd1/bVzvTLwvLDuPG6ru+wou2omOuejOiSfueLduWCbON5YeFuVN9mS91cP9tU - NdpOLtZGJs5EJcVBI74/Irk9Ia86H6M2HZ40HJszHJIwGosuGX4qF3wpFnkoFnUnFXImFWkjE2kjE2gj - E2QhEmMhEmEhElwfEVoeEFoeEFcdEFcdEFUcD1IbD1IbD04aDk0aDkcYDUYYDUQWDEIWDEQWDD8VDD8V - DD0UCzwUCzoUCzoUCzwUCzYSCjUSCjUSCjMRCTMRCTMRCTMRCTMRCTMRCTEQCSwPCCsOCCkOCCsOCCsO - CCkOCCcNByUNByUNByQMByILBiALBiILBiILBh0KBnFqaf////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////729vQkDAgkDAgkDAgkDAgkDAgkDAgMBAQMBAQIBAQMBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9hHJthHJtlJKdZGJtZGJtNGJtlIJ9ZGJtZGJtFFJdVG - JtFFJdFFJc5EJdVGJs5EJdFFJdhHJtlIJ9hHJthHJtFFJdVGJtNGJtVGJtVGJtNGJtlIJ9lIJ9lJKdlJ - KdlIJ9pLK9pLK9tSM9xVN9xYOt5gQ99kSN1eQd5iRt9mS+BoTeFuVOJyWeN4YOR9ZuWBauWCbOaJdOiR - femTgOqciu2omfC3qvK/s/K/s/PFuvTJv/XPx/bVzvjb1fjd1/jg2vni3fnh3Pjg2vni3fnh3Pjb1ffZ - 0vXRyfTJv/LDuPG6ru+wouyklOuejOmTgOaJdOWDbeN5YeBsUt9kSN1eQdtSM9pLK9hHJsxDJMdCJL0/ - IrY8IbE7IKU3Hp40HJkyG5szHJEwGoouGYErF3ooFnkoFncnFW8lFGkjE20kFGgjE2EhEl8fEV8fEVgd - EFoeEFcdEFcdEFIbD1AbD1AbD1IbD1IbD04aDksZDksZDkkYDUQWDEIWDEIWDD8VDD0UCzwUCzwUCzoU - CzwUCzYSCjUSCjUSCjMRCTMRCTMRCTMRCTMRCTAQCS4QCSwPCCwPCCsOCCkOCCkOCCkOCCcNBycNByUN - ByQMByILBiILBiALBiILBiALBnFqaf////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////0RBQAcDAgcDAgcDAgcDAgcDAgMBAQMBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9lIJ9lIJ9ZGJtlIJ9hHJtNGJthHJtVGJtNGJtVGJtVGJtNGJtFFJdVGJtVG - JtZGJtNGJtNGJtVGJtVGJtZGJtZGJtNGJtVGJtZGJthHJtNGJtNGJthHJtpLK9pLK9pNLdlJKdpOLtpO - LtxVN9pPMNxXOd1bPt1cP99kSOBoTd9mS+BsUuFuVOFvVeN3XuR8ZeR8ZeaFb+aJdOiPe+mTgOugj+2o - mO6uoO+zpfG6rvLBtvPHvfTKwfXPx/bVzvfX0Pfa0/fa0/jb1fjb1ffZ0vbTy/XNxPPFu/LBtvG6ru+w - ouymluqciumTgOaHcuWBauN7Y+FwV99lSt1cP9tUNdpPMNlIJ8xDJMlCJMJAI7g9IbM7IKo4Hp40HJsz - HJszHJIwGogtGIUsGIMsGIAqF4AqF3koFnUnFW0kFGgjE2YhEl8fEV8fEVwfEVgdEFgdEFcdEFcdEFAb - D1AbD04aDlAbD00aDk0aDkcYDUYYDUcYDUYYDUcYDUcYDUIWDEEWDD0UCz0UCz0UCzwUCzgSCjUSCjUS - CjUSCjUSCjMRCTMRCTMRCTAQCS4QCSwPCCwPCCwPCCsOCCsOCCkOCCcNBykOCCkOCCUNByILBiQMByIL - BiILBiALBnJraf////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////9XU - 1AcDAgkDAgkDAgcDAgUCAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9lJKdVGJthHJtZGJtNGJtlJKdlIJ9NGJtVGJs9EJc9EJc5EJdFFJdhHJtNGJtVGJtFFJdVGJtZG - JtNGJtVGJtVGJtVGJtFFJdFFJdVGJtVGJtNGJtFFJdZGJtZGJtZGJtZGJtpLK9lJKdpOLttRMttUNd1c - P91cP95gQ+BsUuFuVOBrUOBoTeFuVOJyWeJ0W+N3XuR8ZeWBauaJdOeNeeiSfuqaiOyklO2ome6uoPC2 - qfG9sfLDuPTJv/XNxPXPx/bTy/XRyfXRyfbVzvXPx/TJv/LDuPG9sfC2qe6un+yklOqaiOiSfueLduWB - auJ1XOBsUt9mS95gRNxYOtpPMNlIJ9NGJslCJMA/Ir4/IrQ7IKo4HqU3HpszHJkyG5kyG5EwGoUsGIMs - GHwpFnooFnkoFncnFXcnFXAlFG8lFGgjE2QhEmMhEl8fEVoeEFgdEFcdEFMcD1IbD1McD1IbD00aDk0a - DksZDkkYDUYYDUIWDEYYDUIWDEQWDD8VDEEWDEEWDEEWDD0UCz8VDDwUCzoUCzUSCjUSCjMRCTMRCTMR - CTMRCTEQCTAQCS4QCS4QCS4QCSsOCCsOCCkOCCsOCCkOCCUNByQMByILBiQMByILBiALBiALBnJraf// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////2poaAkDAgkDAgcD - AgUCAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9ZGJtlJKdZG - JtZGJtZGJthHJtVGJs9EJdNGJtFFJdFFJdVGJtNGJtZGJtFFJdFFJdNGJtNGJtFFJdNGJtVGJtVGJthH - JtNGJtFFJdVGJtZGJtVGJtZGJthHJtlJKdlIJ9hHJthHJtpNLdpNLdtRMtxVN91aPN5gRN9kSOFuVOBr - UOBrUOBrUOBsUuFuVOJ0W+J0W+J0W+N7Y+WBauaHceaHcumVguqZh+yjk+yklO6uoPC2qfG9sfLDuPLD - uPPHvfTKwfTKwfTLwvTLwvTLwvLAtfG8sO+0p+6uoOyklOqciuiSfuaJdOaFb+N4YOBsUt9kSN1cP91a - PNtSM9lJKdFFJcxDJMRBI70/IrY8IbM7IKo4HqI2HZszHJcyG5kyG5EwGoYsGIAqF3ooFncnFXQnFXQn - FXQnFW0kFGkjE20kFGsjE2YhEmEhElgdEFcdEFcdEFMcD1IbD1AbD04aDk0aDkkYDUsZDksZDkYYDUYY - DUYYDUIWDD8VDDwUCz8VDDwUCzwUCzoUCzwUCzwUCzwUCzoUCzoUCzYSCjMRCTMRCTMRCTMRCTEQCS4Q - CSwPCCwPCCsOCCsOCCkOCCcNBycNBycNByUNByILBiQMByQMByILBiALBnJraf////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////9XU1AkDAgkDAgcDAgUCAQMBAQMBAQIB - AQMBAQIBAQIBAQMBAQMBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9ZGJtZGJtVGJtVGJthHJtVGJtFF - JdVGJtVGJtVGJtNGJtNGJspDJNNGJs5EJc5EJdNGJtFFJdNGJs9EJdFFJc5EJdpLK9hHJtlJKdlJKdpN - LdlJKdpOLtpOLtpOLtpPMNpNLdpNLdpOLtpNLdpOLttSM9tUNd1aPN1bPuBrUOBrUOBrUOFvVeFvVeFv - VeJ0W+FwV+J1XOJ0W+R/aOR/aOaHceaHcueNeemWg+ufjuyjk+6un/C3qvC5rPK/s/LDuPLDuPLDuPLD - uPLDuPLDuPG9sfC2qe2qm+yklOqciuiSfuaJdOWBauJ1XOFuVN5iRt1eQd1bPtpOLtpLK9ZGJs9EJcdC - JL0/Ir0/IrM7ILE7IKg3Hpw0HJcyG5QxG5EwGoYsGIMsGHwpFnooFnUnFXUnFXImFW0kFGkjE2gjE2Qh - EmQhEmQhEmYhEl8fEV8fEVoeEFMcD1McD1IbD04aDk4aDksZDksZDkcYDUYYDUcYDUQWDEEWDEIWDEIW - DD8VDDwUCzwUCzoUCzgSCjgSCjgSCjYSCjUSCjMRCTUSCjYSCjYSCjMRCTMRCTEQCTAQCS4QCSsOCCkO - CCkOCCkOCCkOCCUNByQMByILBiQMByQMByILBiALBnJraf////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////4qIiAkDAgkDAgUCAQMBAQMBAQMBAQMBAQIBAQIBAQIB - AQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9hHJthHJtVGJtNGJtVGJtNGJthHJtNGJtNGJtZGJs9E - JdFFJdVGJtVGJtNGJs9EJdVGJtFFJdVGJtZGJtlJKdlJKdpOLtlJKdlJKdpOLtpLK9pNLdlJKdpOLttR - MttSM9pPMNpOLtpPMNpOLtpOLtpLK9xVN9tSM91cP91eQd5iRt9lSuBrUOBsUuFwV+JyWeBqT+BrUOFw - V+J1XON4YON5YeN7Y+WBauaJdOmVguqciuyjk+2qm++wovC3qvG8sPG8sPG8sPK/s/K/s/G6ru+zpe6s - neyklOqaiOiSfuaHcuWBauN3XuFuVN9mS91cP91aPNpOLtpLK9ZGJs9EJcdCJMJAI7g9IbQ7IK05H6w5 - H540HJkyG5QxG5QxG4suGYAqF4AqF3wpFnooFnkoFncnFXImFWsjE2kjE2gjE2YhEmEhEmMhEmEhEloe - EF4fEVwfEVoeEFcdEFUcD1IbD04aDksZDk4aDkkYDUQWDEQWDEQWDEIWDEQWDEEWDD8VDDwUCzwUCzoU - CzgSCjYSCjgSCjUSCjUSCjMRCTMRCTMRCTMRCTEQCTEQCTEQCTMRCTEQCS4QCSwPCCwPCCsOCCsOCCUN - ByUNByQMByQMByILBiALBiALBnFraf////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////+rq6gkDAgcDAgMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9ZGJtVGJtVGJtZGJtZGJtNGJtNGJtZGJtNGJtZGJs5EJdFFJdVGJtNGJs9E - Jc9EJdVGJthHJtpNLdpOLtpOLtpNLdlJKdlJKdpNLdpOLtlJKdpNLdpOLttSM9tSM9pPMNxVN9pPMNpP - MNtRMttRMttRMtpPMNpOLttUNd1bPt5gQ99kSOBrUOBsUuFvVeBrUOBrUOBrUOBrUOBsUuFvVeFwV+J1 - XOR9ZuaHcueMd+mVguqZh+yklO6sne+ypO+ypO+0p/C2qfC5rPC3qu+0p+6un+yjk+qciuiSfuaJdOR/ - aOJ1XOFvVeBoTd1cP91bPttUNdpLK9ZGJs5EJclCJMA/Irs9IbM7ILM7IKw5H6U3Hpw0HJkyG5cyG5Ew - GoouGYUsGIErF3wpFnooFnUnFXQnFXAlFGsjE2gjE2gjE2gjE2MhEmEhElwfEVgdEFcdEFcdEFcdEFMc - D1cdEFcdEFUcD00aDksZDkcYDUYYDUIWDEEWDEIWDEIWDEQWDEEWDDwUCzoUCzwUCzgSCjgSCjYSCjUS - CjYSCjUSCjMRCTMRCTAQCTEQCTEQCS4QCTAQCS4QCSwPCCwPCCsOCCsOCDAQCS4QCSsOCCcNByUNByQM - ByALBiALBnJraf////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////4qIiAcDAgMBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9hHJtZGJtVGJtFFJdNGJtZGJtNGJtVGJtVGJtFFJdNGJsxDJM5EJcxDJMxDJMxDJNVGJtlJKdpL - K9pLK9hHJtlJKdpLK9pNLdpNLdpLK9lJKdlIJ9pOLtpPMNpOLtpNLdtRMtlJKdpOLtpPMNtRMttRMtpO - LtpOLttRMtxVN91eQd5gRN9kSOBqT+FvVd9lSt9lSuBqT+BoTeBoTeBoTeBrUOFuVOJ0W+R8ZeWDbeaJ - dOiRfemXheyike2omO6sne6uoO+ypO+ypO+ypO6uoO2omeqciuiSfuaHceR/aOJ0W+BrUOBoTd1cP9xX - OdtSM9pNLdVGJsxDJMlCJL4/Ir0/Irg9Ia86H605H6U3Hp40HJszHJcyG5QxG48wGoouGYMsGHooFnoo - FnkoFnUnFXImFXAlFGsjE2YhEmYhEmEhEmQhEmMhElwfEVgdEFcdEFUcD1McD1McD1IbD1AbD1AbD04a - DlIbD1AbD0sZDkcYDUcYDUIWDEEWDD8VDD0UCzwUCzwUCzwUCzgSCjoUCzYSCjUSCjYSCjUSCjMRCTEQ - CTAQCTAQCTEQCS4QCSwPCCwPCCsOCCwPCCsOCCcNBysOCCsOCCkOCCkOCCsOCCkOCCcNByUNB3Rrav// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////X19Sso - JwMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lIJ9hHJtlI - J9NGJtVGJs9EJc9EJdVGJs9EJdNGJtNGJs5EJc9EJcxDJM9EJdlIJ9lIJ9lJKdpOLtpNLdpOLtlJKdlJ - KdlIJ9pNLdlJKdZGJtlIJ9pNLdpLK9pOLthHJtpNLdpNLdpPMNtRMtpOLtpPMNtSM9tUNdpOLttUNd1b - Pt1cP95gRN9mS99mS95gRN9lSt9lSt5gQ95iRt5iRt9mS+BrUOBsUuN3XuN5YeWDbeeLdumTgOqciuug - j+ymlu2ome2qm+2qm+6sne2omeyklOmWg+aJdOWBauN4YOBrUN9lSt1cP91cP9pOLtlJKdNGJs5EJcRB - I8RBI70/Irg9IbM7ILE7IKo4Hqc3HqA1HZszHJcyG5QxG48wGogtGIErF3koFncnFXUnFXUnFW0kFG8l - FGkjE2kjE2YhEmEhEl4fEV4fEVgdEFoeEFcdEFMcD1IbD1UcD1AbD1IbD00aDksZDkcYDUsZDk4aDlAb - D0sZDkQWDEIWDD8VDDwUCzoUCzoUCzgSCjgSCjgSCjYSCjUSCjUSCjUSCjMRCTEQCTMRCTEQCTEQCS4Q - CS4QCS4QCSwPCCsOCCkOCCUNBykOCCcNByUNByQMByQMByILBiALBiALBlBFQ6mlpaqmpamlpamlpaml - pamlpailpailpailpaelpKelpKalpKelpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKal - pKalpKalpKalpKalpKWkpKWkpKWkpKWkpKWkpKWkpKWkpKWkpKWkpKWkpKWkpFdVVQMBAQMBAQIBAQMB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9hHJtZGJtZGJtVGJtNGJtZGJtFF - JdZGJtNGJs5EJcpDJM5EJc9EJdFFJdNGJtpOLttRMtpNLdpLK9pLK9hHJtZGJtZGJthHJthHJtlIJ9hH - JtlIJ9lJKdlIJ9lIJ9lIJ9pOLtpOLtpLK9pOLtlJKdpOLttRMttSM9tSM9tRMtpOLt1cP91aPN5gQ95g - Q91eQd5iRt5gRNxVN95gQ91cP91eQd5gQ+BrUOBsUuJ0W+R8ZeWCbOiPe+mXheqciuugj+yklOymluym - luyklOugj+ugj+eNeeWBauN3XuBrUN9mS91cP9xVN9tRMtpLK89EJclCJMRBI8JAI70/Irs9Ibg9Ia05 - H6g3HqA1HZ40HJw0HJszHJIwGo0uGYouGYMsGIAqF3wpFnUnFXUnFXQnFW0kFGkjE2kjE2YhEmYhEl8f - EWEhEl8fEVoeEFgdEFUcD1UcD1UcD1IbD1AbD04aDk0aDksZDksZDksZDkYYDUkYDUsZDkcYDUcYDUQW - DEEWDD0UCz0UCzwUCzgSCjUSCjYSCjYSCjUSCjMRCTMRCTEQCTEQCTAQCTEQCS4QCSwPCCwPCC4QCSwP - CCsOCCcNBycNBycNBycNByQMByILBiALBiILBiILBiALBiALBiALBh0KBh0KBh8LBh0KBh8LBh8LBh0K - BhgJBRYHBBEGAxEGAxEGAxEGAxMHBBEGAw8FAw8FAxEGAxEGAw8FAw8FAw4FAwwFAw4FAwwFAwwFAwwF - Aw4FAwwFAwoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgMBAQUCAQUCAQMBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9hHJthHJtZGJthHJtVGJthHJtlIJ9VGJs5EJdFFJdFF - JcxDJMxDJNVGJtlIJ9pLK9pOLtpOLtpNLdpLK9ZGJtFFJdlIJ9lIJ9lIJ9VGJtZGJthHJtlIJ9VGJthH - JthHJthHJtlIJ9lJKdlJKdpOLttSM9tSM9tUNdtRMtpNLdpNLdtUNdxYOtxXOd1bPt1aPN5gRNxYOtxX - Od1aPN1aPNtUNdxXOd5gQ99lSuBsUuFvVeN7Y+aHcueNeemVguqZh+uejOyikeyikeufjuqaiOmXheaH - ceN5YeFwV99kSN1eQdtUNdpPMNlIJ9NGJspDJMRBI74/Ir0/Irs9IbM7IK05H6o4Hqc3HqI2HZszHJsz - HJQxG40uGY0uGYUsGIErF3koFnkoFnooFnUnFXImFW8lFGsjE2kjE2gjE2QhEmMhEl8fEVwfEVoeEFcd - EFUcD1McD1UcD1AbD1AbD04aDkkYDUsZDkkYDUkYDUQWDEIWDEQWDEQWDEQWDEQWDEYYDUQWDEEWDD8V - DDoUCzgSCjYSCjUSCjYSCjUSCjMRCTMRCTEQCTAQCS4QCS4QCS4QCS4QCSsOCCkOCCkOCCcNByUNByUN - ByUNByQMByQMByQMByILBiALBh8LBh0KBh0KBh0KBhsJBRsJBRsJBRsJBRoJBRgJBRoJBRsJBRgJBRYH - BBMHBBMHBBMHBBEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAwwFAw4FAwwFAwoDAgoDAg4FAwoDAgoDAgoD - AgoDAgkDAgkDAgkDAgkDAgkDAgcDAgMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9ZGJthHJthHJthHJthHJthHJtVGJtZGJtVGJsxDJM5EJc5EJdVGJtpOLtpO - LtZGJtlJKdlIJ9lJKdhHJtlJKdhHJtNGJs5EJdNGJtVGJtlJKdlJKdpLK9lJKdlIJ9pLK9ZGJtVGJtVG - JttUNeBoTd9kSN9lSt5gRN1bPtpOLttRMtpLK9pPMNtSM9tSM9xXOd1aPNxVN9tUNdpPMNpOLtpOLttS - M91bPt5gRN9lSuBrUON3XuR/aOeMd+iRfemXheqciuqciuqaiOmXhemVgumVguWBauJ0W+BoTd1cP9tU - NdpOLthHJs9EJclCJMVBI7s9Ibs9Ibk9IbE7ILE7IKg3HqI2HaA1HZszHJcyG5cyG40uGYouGYYsGIAq - F3wpFnooFnkoFnImFXImFXUnFW0kFGkjE2kjE2YhEmYhEmQhEmEhElwfEVwfEVcdEFUcD1UcD1cdEFIb - D04aDksZDk0aDk0aDksZDkkYDUIWDEIWDEYYDUQWDEIWDEIWDEEWDD0UCz0UC0QWDEQWDD8VDDgSCjYS - CjUSCjMRCTEQCTEQCTAQCTAQCS4QCTAQCS4QCS4QCSsOCCsOCCcNBycNByUNByUNBycNByQMByILBiQM - ByILBiALBiILBiALBh0KBh8LBhsJBRsJBRoJBRoJBRoJBRgJBRYHBBgJBRYHBBQHBBMHBBEGAxMHBBYH - BBYHBBMHBBEGAw8FAxEGAxEGAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgkD - AgkDAgkDAgcDAgUCAQMBAQIBAQIBAQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJthHJtZGJtNGJtZGJtlJKdNGJtVGJtFFJdNGJs5EJdVGJtlIJ9pLK9lJKdVGJthHJthHJtpL - K9lJKdlIJ9lIJ9hHJtlJKdhHJtpLK9lJKdlJKdpLK9lJKdhHJtlJKdlIJ9hHJttSM95gROBqT+BrUN5i - Rt9lSt5gRNtSM9lJKdhHJtlIJ9lJKdpLK9tRMtpOLtpOLtpOLtlJKdlIJ9hHJtpNLdtSM91aPN1eQd9m - S+BsUuR8ZeWCbOeMd+mTgOmVguqZh+qZh+mVgueNeeeLduR8ZeBrUN5iRttUNdlIJ9hHJs5EJcpDJMJA - I8A/Irk9Ibg9IbE7IK86H6w5H6g3HqI2HZszHJcyG5YyG5EwGogtGIYsGIMsGIErF4AqF3koFncnFXUn - FXImFW8lFG8lFGsjE2gjE2YhEmYhEl8fEVwfEV4fEVgdEFcdEFUcD1UcD1IbD1IbD00aDk0aDk4aDk0a - DksZDkcYDUYYDUQWDEIWDEIWDEEWDD8VDD8VDDwUCzwUCzwUCzoUCzwUCzwUCz0UCzoUCzUSCjMRCTMR - CTEQCTAQCS4QCTAQCS4QCS4QCSwPCCsOCCcNBycNByUNBycNByUNByQMByQMByQMByILBiALBiILBiAL - Bh0KBhsJBRsJBRsJBRoJBRsJBRoJBRgJBRYHBBYHBBYHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxMHBBMH - BBEGAxEGAxEGAxEGAw8FAw4FAw4FAwwFAwwFAwwFAwoDAgkDAgkDAgoDAgkDAgcDAgkDAgkDAgcDAgUC - AQIBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9ZGJtVGJtZG - Js5EJdNGJtZGJtFFJc9EJc9EJc5EJdNGJtZGJtpLK9lJKdlJKdZGJtpNLdZGJtVGJtZGJtpLK9lIJ9lJ - KdpLK9ZGJthHJtlIJ9lIJ9lIJ9VGJtpLK9pNLdlJKdpOLtxYOuFuVON5YeN4YOBrUN9kSN5gRNxXOdlI - J9lJKdVGJtNGJtlIJ9lIJ9hHJtlIJ9hHJtZGJtVGJtlJKdlJKdpLK9pPMNxYOt5gQ99mS+J1XOR9ZuaH - cueMd+iPe+mVgumWg+iRfeaJdOaHceN3Xt9lSt1aPNpOLthHJs9EJcpDJMJAI74/Ir0/Irs9IbQ7IK86 - H686H6c3HqA1HZw0HJw0HJYyG5EwGosuGYgtGIMsGIMsGIAqF3ooFncnFXcnFXQnFXImFW8lFG0kFGkj - E2gjE2gjE2MhEmEhEl4fEVwfEVgdEFcdEFUcD1UcD1IbD1McD04aDk4aDk4aDksZDkkYDUcYDUcYDUYY - DUIWDEEWDD8VDD8VDD0UCz0UCzoUCzoUCzgSCjgSCjgSCjYSCjgSCjoUCzoUCzYSCjUSCjMRCTMRCTEQ - CTAQCSwPCCsOCCsOCCkOCCcNByUNByUNByQMByQMByQMByQMByALBiILBiILBiALBh8LBh0KBh0KBh0K - BhsJBRoJBRsJBRoJBRoJBRgJBRYHBBgJBRQHBBMHBBEGAxEGAw8FAxEGAxEGAxEGAw8FAxEGAw8FAxEG - AxEGAxMHBBEGAw8FAwwFAwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgUCAQUCAQMBAQMBAQIBAQMB - AQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9lIJ9ZGJtZGJtNGJtVGJtVGJs9E - JcxDJMxDJM5EJdVGJthHJtpLK9lJKdlJKdZGJtpLK9lIJ9lIJ9pNLdlJKdpLK9hHJthHJtNGJtVGJtZG - JtlJKdlIJ9lJKdhHJtpOLttUNd1cP+BrUON7Y+aJdOaHceFwV95gRN5gQ9tSM9ZGJs5EJdFFJcxDJM5E - Jc5EJdlIJ9FFJc5EJc5EJdFFJcxDJNNGJtlJKdpNLdpOLt1aPN1eQeFwV+N4YOWCbOaHcueMd+iRfeiR - feeNeeaFb+R/aOJyWd1cP9pPMNpNLc5EJcpDJMJAI70/Irg9IbQ7ILQ7IK05H6o4Hqo4HqI2HZ40HJsz - HJkyG5EwGosuGYUsGIYsGIMsGH4qF3koFnkoFncnFXcnFXAlFHAlFG8lFGsjE2gjE2QhEmYhEmQhEmEh - El8fEVwfEVwfEVcdEFUcD1McD1IbD1AbD1AbD04aDkkYDUkYDUkYDUQWDEQWDEQWDEQWDEIWDD8VDD0U - Cz0UCz0UCzwUCzwUCzgSCjYSCjgSCjYSCjUSCjMRCTMRCTUSCjUSCjYSCjYSCjMRCTEQCTAQCSwPCC4Q - CSsOCCcNBycNByUNByUNByUNByQMByQMByALBiILBh8LBh8LBh8LBh0KBh0KBhsJBRoJBRoJBRoJBRgJ - BRYHBBgJBRgJBRYHBBQHBBMHBBEGAxEGAxEGAxEGAxEGAw8FAw4FAw4FAwwFAw4FAw4FAw4FAwwFAwwF - AwwFAwwFAw4FAw8FAw8FAwwFAwkDAgkDAgkDAgcDAgUCAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAA - AAIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9NGJtZGJtNGJtZGJtNGJtVGJtFFJc5EJc5EJc5EJdZG - JtlIJ9lIJ9ZGJtZGJtVGJthHJtlJKdpNLdpLK9hHJs9EJdNGJtNGJs9EJdFFJdlIJ9VGJtZGJtlIJ9pL - K9pOLt1eQd9mS+FuVOWBauaHceWBauBsUt5iRt1cP9pNLc9EJdNGJs5EJcRBI8pDJMVBI9NGJslCJM9E - Jc5EJcpDJMRBI8RBI8lCJNFFJdpLK9tRMt1cP+BoTeJ0W+R9ZuaHcuaJdOeNeeiPe+aJdOR/aON4YOBr - UNtUNdlIJ89EJcdCJL4/Irs9IbY8IbE7IK05H686H6g3HqM2HZw0HKI2HZszHJQxG5EwGo0uGYUsGIUs - GIErF34qF3wpFnkoFnkoFncnFXUnFW8lFGsjE2kjE2QhEmgjE2YhEmEhEmYhEmQhEmEhElwfEVoeEFcd - EFcdEFMcD1IbD1IbD04aDk4aDk4aDkkYDUkYDUYYDUQWDEIWDEIWDEEWDD8VDDwUCz0UCz0UCzoUCzoU - CzgSCjYSCjYSCjUSCjUSCjMRCTMRCTMRCTEQCTMRCTMRCTMRCTMRCTAQCS4QCS4QCSwPCCcNByUNByUN - ByUNByUNByQMByILBiALBiALBh8LBh8LBh0KBhsJBR0KBhsJBRgJBRoJBRoJBRoJBRgJBRgJBRgJBRgJ - BRQHBBQHBBEGAxMHBBEGAw8FAw8FAw8FAw8FAw4FAwwFAwwFAw4FAw4FAwoDAgoDAgoDAgoDAgwFAwwF - AwwFAwwFAwwFAwwFAwoDAgkDAgcDAgUCAQUCAQUCAQMBAQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9lJKdZGJtVGJtZGJtZGJs9EJdNGJtFFJc9EJdZGJtZGJtpLK9lJKdhHJthH - JthHJtlJKdlIJ9hHJthHJtZGJs9EJc9EJcxDJM9EJdNGJtFFJc5EJdNGJs9EJdZGJtpPMN1bPt5gROFu - VON5YeN7Y+FwV95iRt5gQ9tUNc5EJcpDJMxDJM5EJcdCJMJAI8RBI8VBI8VBI8dCJMxDJMlCJL0/IsJA - I8RBI85EJdZGJtlJKdpPMN5gQ+BsUuN4YOWBauaHcuaJdOeLduaHceN7Y+J0W99lStpNLcxDJMdCJMA/ - Irs9Ibg9IbM7IKo4Hqo4Hqw5H6g3HqM2HZszHJcyG5EwGoouGYsuGYgtGIUsGIMsGIErF4AqF3ooFnwp - FncnFXkoFnAlFHQnFW8lFGgjE2YhEmgjE2kjE2QhEmMhEmMhEl4fEVwfEVwfEVUcD1UcD1IbD1McD1Ib - D1AbD04aDk0aDkkYDUkYDUcYDUQWDEIWDEIWDEEWDD8VDD0UCz0UCzwUCzgSCjoUCzgSCjgSCjUSCjUS - CjUSCjMRCTMRCTMRCTEQCTAQCTEQCTAQCS4QCSwPCDEQCTEQCSwPCCcNBycNBykOCCcNBycNByILBiAL - Bh8LBh8LBh8LBiALBiALBh0KBh0KBhsJBRgJBRgJBRgJBRgJBRgJBRgJBRoJBRYHBBQHBBMHBBMHBBMH - BBEGAxEGAxEGAxEGAw8FAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwoDAgoDAgoDAgkDAgoDAgwFAwoDAgkD - AgkDAgcDAgUCAQUCAQoDAgoDAgcDAgMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJtZGJtNGJtlJKc9EJdVGJspDJNFFJc9EJdlIJ9pNLdlIJ9hHJtVGJtZGJtVGJtZGJtlJKdlI - J9VGJtZGJtNGJs9EJcxDJMpDJM5EJcpDJNFFJc5EJdFFJdlIJ9tRMt1bPt5gQ99kSOBsUuBoTd1bPttS - M9lIJ9FFJcdCJMdCJMxDJM5EJcJAI70/Ir4/Ir0/Ir4/Ir0/IsA/IsdCJMJAI8JAI8A/IsVBI8pDJNFF - JdpLK9tUNd9lSuJ0W+R8ZeaFb+aHceaHcuWDbeN4YOBsUt5iRtlJKclCJMA/Irk9Ia05H7M7IKw5H6g3 - HqI2HaU3HqM2HaM2HZYyG5QxG5IwGosuGYYsGIgtGIErF4AqF4ErF3koFnkoFncnFXcnFXImFW8lFHIm - FW0kFGsjE2sjE2gjE2MhEmEhEmMhEl8fEV4fEV4fEVgdEFcdEFcdEFMcD1IbD04aDk0aDk0aDksZDkYY - DUYYDUYYDUYYDUIWDEQWDEEWDD8VDD8VDD0UCz8VDDwUCzgSCjgSCjYSCjYSCjYSCjUSCjUSCjMRCTMR - CTAQCS4QCS4QCSwPCC4QCSwPCCwPCC4QCS4QCSwPCCwPCCsOCCsOCCcNByQMByILBiALBh8LBiALBiAL - Bh8LBh0KBh0KBhsJBRoJBRoJBRoJBRgJBRgJBRgJBRgJBRgJBRYHBBQHBBQHBBMHBBEGAw8FAxEGAxEG - Aw8FAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwoDAgkDAgkDAgoDAgkDAgkDAgoDAgkDAgUCAQMBAQUC - AQUCAQUCAQMBAQUCAQUCAQMBAQcDAgcDAgUCAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9FFJdVGJthH - JtZGJtNGJtNGJtFFJc9EJdFFJdNGJtpNLdpNLdpLK9lIJ9FFJdZGJthHJtZGJtZGJtlIJ9FFJc5EJc9E - Jc5EJclCJMlCJMxDJNFFJc5EJc5EJdZGJtpLK9xXOd5gQ91eQd1cP9tSM9VGJtFFJcdCJMRBI8JAI8RB - I8RBI8RBI8JAI7s9IbQ7ILg9IbY8Ibk9Ib0/Irk9Ibk9Ib0/Ir0/IsRBI8VBI89EJdlIJ9tRMt5gQ+Fu - VON3XuWCbOWCbOaFb+WBauJyWd5gRN1aPNZGJsVBI7g9IbY8Iag3Hqg3HqU3HqU3HqA1HaA1HZw0HJcy - G5IwGo0uGYouGYgtGIYsGIUsGIAqF34qF3ooFnkoFnkoFnkoFncnFW8lFG8lFGsjE2kjE2kjE2gjE2Yh - EmMhEl8fEV8fEV4fEV4fEVoeEFgdEFcdEFcdEFMcD1McD1McD1AbD0sZDkkYDUkYDUcYDUcYDUcYDUQW - DEQWDEEWDD8VDD8VDD0UCzwUCzoUCzoUCzoUCzgSCjgSCjYSCjUSCjMRCTMRCTMRCTEQCTAQCTAQCSwP - CC4QCSwPCCwPCCsOCCkOCCkOCCkOCCsOCCwPCCkOCCUNByQMByQMByALBh8LBh8LBh0KBh0KBh8LBhsJ - BRsJBRsJBRoJBRoJBRoJBRgJBRYHBBQHBBYHBBQHBBQHBBQHBBEGAw8FAw8FAxEGAw4FAw8FAw4FAw4F - AwwFAwwFAwoDAgoDAgkDAgoDAgkDAgkDAgoDAgkDAgkDAgkDAgkDAgcDAgUCAQMBAQMBAQMBAQIBAQMB - AQMBAQMBAQMBAQMBAQMBAQUCAQUCAQUCAQMBAQMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9hHJtVGJtVGJthHJtVGJtVGJs5E - Jc5EJdFFJdVGJtlJKdlIJ9pLK9ZGJtNGJtlJKdhHJtNGJtNGJtNGJspDJNFFJdFFJc9EJclCJMJAI8VB - I8pDJMpDJMdCJM9EJdVGJtpLK9tRMtpOLtpNLdlJKc5EJcpDJMRBI8JAI70/IsA/IsVBI8A/Ir0/Irg9 - Ibk9IbY8Ibg9Ibs9Ibk9Ibk9IbQ7ILE7ILM7ILk9Ib4/IspDJNNGJtpNLd1cP+BqT+J1XOR8ZeWBauWC - bOR9ZuBsUt1eQd1bPsxDJLs9IbY8IbQ7IKw5H6g3HqM2HZ40HJkyG5IwGpYyG5IwGpEwGosuGYYsGIUs - GIYsGIErF4ErF34qF34qF3cnFXUnFXImFXImFXAlFG0kFG0kFGkjE2gjE2YhEmMhEmEhEmEhEmEhEl4f - EWEhEl8fEVcdEFcdEFUcD1IbD1UcD1McD04aDkkYDUkYDUkYDUkYDUkYDUcYDUQWDEQWDEIWDEEWDD0U - Cz0UCz0UCzwUCzoUCzwUCzgSCjYSCjYSCjMRCTMRCTMRCTEQCTEQCTEQCTEQCS4QCS4QCSsOCC4QCSsO - CCkOCCcNBycNByUNByUNByQMBycNByUNBykOCCUNByQMBx8LBh0KBh0KBh0KBhsJBRoJBRgJBRoJBRoJ - BRsJBRgJBRgJBRgJBRYHBBQHBBQHBBQHBBMHBBEGAxEGAxEGAxEGAw4FAw4FAw4FAwwFAwwFAwoDAgoD - AgoDAgkDAgoDAgcDAgoDAgkDAgcDAgoDAgcDAgMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9NGJtVGJs9EJdFFJdVGJtFFJc5EJdFFJc9EJdhHJtlJ - KdlJKdVGJtVGJthHJtZGJtNGJtNGJtNGJtFFJcpDJM9EJcpDJMdCJMRBI8dCJMRBI8xDJMlCJMlCJMdC - JM5EJc9EJdVGJtNGJtFFJcpDJNNGJsRBI8RBI74/Irs9Ib0/Ir0/IsJAI7g9IbE7ILE7IK05H7M7ILE7 - ILE7IK86H7E7IKw5H605H605H7M7ILs9IcVBI9lJKdxXOeBqT+J0W+N7Y+WBauWCbOR9ZuBqT9xYOtxV - N8xDJLk9Iaw5H605H6U3Hpw0HJszHJcyG5IwGpEwGo8wGo0uGZEwGoYsGIMsGIMsGIErF4AqF4AqF3wp - FnkoFncnFXImFXImFXAlFG0kFGkjE2gjE20kFGgjE2MhEmMhEmQhEl8fEV4fEV8fEVoeEFgdEFcdEFUc - D1cdEFMcD1IbD1IbD1AbD0kYDU4aDksZDkcYDUcYDUcYDUQWDEEWDEIWDD8VDD0UCz8VDD0UCzwUCzoU - CzgSCjgSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTAQCS4QCS4QCSwPCCwPCCsOCCsOCCkOCCcNBycNByUN - BycNByUNByQMByILBiQMByQMBycNByQMByILBh8LBh8LBh0KBhoJBRoJBRoJBRoJBRoJBRYHBBYHBBYH - BBYHBBQHBBMHBBMHBBMHBBMHBBEGAw8FAw8FAw8FAw4FAw4FAwwFAwwFAw4FAwoDAgwFAwoDAgwFAwoD - AgkDAgkDAgkDAgkDAgUCAQMBAQMBAQMBAQUCAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////89EJdFFJdhHJtFFJdVGJtFFJdFFJcxDJM5EJdNGJtZGJtlIJ9hHJtVGJtNG - JtlIJ9VGJtNGJs5EJcxDJMdCJMpDJMlCJMpDJMVBI8lCJMlCJMdCJMA/IsJAI70/IsVBI8RBI8dCJMdC - JNNGJs5EJcVBI8JAI70/Ir0/Ir4/Irk9Ibk9Ib4/Irg9IbM7IKo4Hqc3Hqw5H6o4Hqg3HqU3Hqg3Hqc3 - Hqo4Hq86H6w5H7Q7IMA/Is5EJdpOLt1eQeFvVeN3XuR8ZeN7Y+N3Xt9kSNpPMNpOLsA/Iqo4HqI2HaA1 - HZ40HJcyG5cyG5IwGpIwGpEwGoouGYUsGIUsGIUsGIErF4UsGHwpFnwpFncnFXkoFnkoFncnFXUnFXAl - FGsjE2kjE2sjE2kjE2gjE2MhEmQhEmEhEmEhEl8fEV8fEV4fEV4fEVcdEFUcD1UcD1McD1McD1IbD1Mc - D04aDk4aDksZDkkYDUkYDUcYDUYYDUQWDEEWDEIWDD8VDEEWDD8VDD0UCz0UCzgSCjgSCjgSCjUSCjMR - CTUSCjMRCTMRCTEQCTEQCTAQCS4QCS4QCSsOCCwPCCsOCCkOCCsOCCcNBysOCCcNBycNByILBiQMByQM - ByILBiALBiALBiALBiILBiILBiILBiALBh0KBh0KBhsJBRsJBRoJBRYHBBYHBBQHBBgJBRQHBBMHBBQH - BBMHBBMHBBEGAxEGAw8FAw4FAw8FAw4FAw4FAwwFAwwFAwoDAgoDAgkDAgoDAgoDAgkDAgkDAgoDAgcD - AgUCAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9NGJtVGJtVGJtZGJtVGJtZGJsxDJMxDJM5EJdhHJtlIJ9VGJtFFJc9EJdZGJtZGJtNGJs9EJc9E - JcpDJMRBI8lCJMRBI8lCJL4/IsVBI8JAI70/IsA/Ir0/Ir4/Ir4/Ir4/IsA/IsJAI8RBI74/Ir4/IsA/ - Irk9Ibs9Ibs9Ibk9Ibs9Ibk9IbM7IKo4Hqg3HqU3HqU3HqA1HaM2HaM2HaM2HaA1Hac3Hqg3Hqg3Hq05 - H7Q7IMdCJNhHJt1aPOBqT+J1XON4YON4YOFwV91bPtlIJ9ZGJr0/Iqo4HqI2HZ40HJszHJIwGpEwGpEw - GpYyG4ouGYouGYUsGIUsGIMsGIAqF4ErF3ooFnooFncnFXkoFnkoFnUnFXImFXImFWkjE2kjE2kjE2kj - E2gjE2MhEmQhEmMhEmEhEmQhEl8fEV8fEVwfEVgdEFcdEFcdEFUcD1McD04aDk4aDk4aDk0aDlAbD0sZ - DkcYDUQWDEYYDUIWDEEWDD8VDEEWDD8VDD0UCz0UCzoUCzoUCzoUCzgSCjUSCjMRCTYSCjMRCTEQCTAQ - CTMRCTEQCTAQCTAQCSwPCCsOCCsOCCkOCCkOCCsOCCkOCCkOCCkOCCILBiALBiALBiILBiALBiALBiAL - Bh8LBh0KBh0KBiALBiALBiILBh8LBh0KBhoJBRgJBRgJBRQHBBYHBBQHBBQHBBMHBBMHBBMHBBMHBBMH - BA8FAw4FAw8FAw4FAwwFAwwFAwwFAwoDAgwFAwkDAgwFAwoDAgkDAgkDAgkDAgcDAgUCAQUCAQMBAQMB - AQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9FFJdFFJdlI - J9VGJtFFJc9EJc5EJcVBI8pDJNFFJdVGJtVGJtNGJs5EJdNGJtZGJs9EJc9EJdNGJslCJMJAI8RBI8RB - I70/IsA/Ir0/Ir4/Ir0/Irs9Ibs9Ib4/IsA/Ir0/Ir4/IsRBI74/Irs9Ib0/Irk9Ibg9Ibk9Ibg9IbQ7 - ILY8IbY8Ia05H6g3HqM2HaA1HaA1HaI2HaU3Hqc3HqM2HZw0HKA1HaI2Hao4Hq86H7E7IL0/Is9EJdtU - Nd9kSOFvVeN3XuJ1XOBsUtxVN9FFJclCJLk9IaA1HZkyG5cyG5YyG5QxG48wGoouGY8wGoUsGIgtGIUs - GIAqF34qF4AqF34qF3ooFnooFnkoFncnFXQnFXQnFXAlFHAlFHAlFGkjE2kjE2gjE2kjE2QhEmMhEmMh - EmMhEmMhEl8fEV4fEVoeEFUcD1McD1IbD1McD1IbD00aDk0aDksZDk0aDk0aDkkYDUkYDUkYDUcYDUIW - DEEWDEEWDD8VDD0UCz0UCz0UCzwUCzwUCzoUCzgSCjYSCjYSCjMRCTMRCTMRCTMRCTMRCTEQCTAQCTAQ - CSwPCCsOCCsOCCsOCCkOCCkOCCcNByUNBycNByILBiILBiALBiALBiILBh8LBh8LBh8LBh8LBh0KBh0K - Bh0KBh0KBh0KBiALBh0KBhsJBRoJBRoJBRYHBBYHBBQHBBQHBBMHBBMHBBEGAxEGAxEGAw8FAw8FAw4F - AwwFAwwFAwwFAwwFAwoDAgoDAgwFAwoDAgoDAgkDAgkDAgUCAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIB - AQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9NGJs9EJdNGJtNGJtVGJtNGJsxD - JMpDJMxDJNNGJtNGJtZGJtNGJtNGJtZGJthHJs5EJcxDJM9EJcVBI8RBI8JAI8RBI8JAI8JAI7s9IcA/ - Irs9Ibg9Ib0/Ir0/Ir0/Ir0/Ir4/IsJAI74/Irk9Ib0/Irk9Ibk9Ibg9IbQ7ILE7ILY8Ia86H605H6A1 - Hag3HqU3Hp40HJ40HKA1HZszHKM2HZszHJ40HKM2Hag3Hqo4Hqw5H7Y8IcRBI9pOLt9kSOBsUuN5YeFv - Vd9mS9pOLsVBI8RBI7Q7IJIwGpIwGpIwGpEwGo0uGYYsGIouGYouGYErF4ErF4ErF4AqF4AqF34qF4Er - F3ooFncnFXcnFXUnFXQnFXImFXAlFG0kFGsjE2sjE2YhEmYhEmYhEmQhEmYhEmMhEmEhEmEhElwfEVwf - EVoeEFMcD1AbD1cdEFMcD04aDk0aDk0aDksZDksZDkkYDUkYDUkYDUcYDUYYDUcYDUQWDEEWDD8VDD8V - DD8VDD0UCzoUCzoUCzgSCjgSCjgSCjUSCjMRCTMRCTMRCTMRCTMRCTEQCTEQCS4QCSsOCCsOCCwPCCsO - CCsOCCcNByQMByUNByUNByILBiILBiILBiQMByILBh8LBh8LBh0KBh8LBh8LBh0KBhsJBRsJBRsJBRoJ - BRgJBRoJBRoJBR0KBhgJBaelpP///////////////////////////////////////6alpAwFAw4FAwwF - AwwFAwwFAwoDAgoDAgkDAgkDAgkDAgUCAQMBAQMBAQIBAQIBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9NGJtVGJtNGJtZGJs9EJcpDJM5EJc5EJcdCJNFFJdFF - JdNGJtNGJtFFJdZGJtNGJs5EJcpDJMVBI8dCJMA/IsA/IsA/IsJAI70/IsJAI74/Ir0/Irg9Ibk9Ibs9 - Ibk9Ibs9Ibs9Ib0/IsA/Irk9Ibk9IbY8Ibg9IbM7ILY8IbE7IK86H6o4HqU3Hp40HKM2HZ40HJszHJsz - HJkyG5szHJszHJcyG5YyG6I2HaI2Hag3HqI2Haw5H74/ItlIJ91bPuBrUOJyWeFuVN9lSthHJr4/Irg9 - Ia86H5IwGoouGY0uGYsuGYouGYUsGIUsGIUsGIUsGHooFnkoFn4qF3ooFnooFoAqF3ooFnUnFXImFXQn - FW8lFHAlFG8lFGsjE2sjE2sjE2kjE2YhEmYhEmkjE2YhEmQhEmEhEl8fEV4fEV4fEVgdEFUcD1McD1cd - EFcdEE4aDk0aDksZDk0aDksZDksZDkkYDUcYDUQWDEYYDUQWDEIWDD8VDD8VDD8VDD0UCzwUCzoUCzoU - CzYSCjYSCjYSCjYSCjMRCTMRCTMRCTEQCTEQCTEQCTEQCTAQCSwPCCsOCCsOCCsOCCkOCCkOCCcNByUN - ByUNByILBiALBiILBiQMByILBiALBh8LBh0KBh0KBh0KBhsJBRsJBRsJBRoJBRoJBRYHBBgJBRgJBRgJ - BRgJBailpf///////////////////////////////////////6alpA4FAw4FAwwFAw4FAwwFAwoDAgoD - AgkDAgkDAgUCAQUCAQUCAQUCAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9ZGJtFFJdFFJdFFJdZGJtFFJcpDJMpDJMVBI8xDJM9EJc9EJdFFJdNGJtNG - JtFFJdFFJc9EJcxDJMpDJMdCJL4/Ir4/Ir0/Ir4/IsdCJMA/IrQ7ILk9Ibg9Ibg9Ibg9Ibk9Ibg9Ibk9 - Ibs9IbM7ILQ7ILQ7ILM7IK86H6w5H6o4Hqo4Hqw5H6M2HaA1HZ40HJkyG5QxG5szHJw0HJszHJYyG5Iw - GpcyG5w0HKA1HaA1HZw0HKM2HbQ7IMpDJNxYOuBoTeFwV+BrUN1cP85EJbM7ILE7IKw5H40uGYgtGIYs - GIYsGIouGYYsGIMsGIMsGIAqF4AqF3koFnooFnkoFnwpFnooFnooFnUnFXAlFG8lFHAlFG8lFG0kFGsj - E2sjE2gjE2gjE2QhEmQhEmYhEmEhEmEhEl8fEV8fEWEhElwfEVgdEFcdEFIbD1cdEFMcD1IbD1AbD00a - Dk0aDk0aDkkYDUkYDUkYDUYYDUYYDUQWDEQWDEcYDUEWDD8VDD0UCz0UCzwUCzgSCjUSCjYSCjUSCjMR - CTMRCTMRCTEQCTMRCTEQCTAQCSwPCC4QCSwPCCwPCCsOCCsOCCsOCCkOCCUNByQMByUNByQMByQMByIL - Bh8LBh8LBiALBiALBh8LBh0KBhsJBRoJBRoJBRoJBRoJBRoJBRgJBRgJBRoJBRYHBBgJBaelpP////// - /////////////////////////////////6alpAwFAwwFAwwFAwwFAwoDAgoDAgkDAgcDAgkDAgcDAgUC - AQUCAQMBAQUCAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9NGJtFFJdVGJs9EJdVGJsxDJMlCJMxDJMdCJMxDJNFFJdFFJcxDJMlCJM5EJdNGJs5EJc5EJcpD - JMdCJMVBI74/Ir0/Irs9Ib0/IsRBI7k9Ib0/Irg9IbY8IbY8Ibk9IbY8Ibs9IbQ7ILY8Ibg9Iaw5H6w5 - H6w5H6o4Hqo4Hq86H6g3HqI2HZw0HJ40HJkyG5kyG5w0HJQxG5cyG5QxG5QxG5IwGpcyG5kyG5kyG5sz - HJQxG5szHKo4Hr0/ItpLK95gROBrUOBoTdtUNcJAI6M2Hac3Hqg3HpIwGoouGYYsGIUsGIYsGIMsGHwp - FnooFnwpFnwpFnooFnkoFnkoFncnFXkoFnkoFncnFXAlFHImFXAlFHAlFG8lFGsjE2kjE2gjE2YhEmQh - EmMhEmMhEmEhEmEhEl8fEV8fEV8fEVwfEVgdEFUcD1UcD1McD1IbD1IbD04aDk0aDk0aDk0aDkkYDUcY - DUkYDUYYDUYYDUQWDEIWDEIWDEIWDD8VDD0UCzwUCzoUCzoUCzUSCjUSCjMRCTUSCjMRCTEQCTMRCTMR - CTMRCTEQCS4QCTAQCS4QCSwPCCsOCCsOCCkOCCcNBycNBycNByUNByQMByUNByQMByALBh8LBh8LBh8L - Bh8LBh0KBhsJBRsJBR0KBhsJBRgJBRYHBBYHBBYHBBYHBBQHBBYHBKelpP////////////////////// - /////////////////6alpAwFAw4FAwwFAwwFAwwFAwwFAwoDAgcDAgkDAgcDAgMBAQMBAQUCAQIBAQIB - AQMBAQMBAQMBAQMBAQIBAQIBAQAAAAIBAQAAAAAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9ZGJtFFJdNG - Js9EJdFFJdVGJs9EJclCJMpDJMVBI9FFJdZGJs5EJdFFJc5EJdFFJdFFJdNGJslCJMVBI8A/Ir0/Ir0/ - Irg9Ibs9Ib0/IsJAI7s9Ibk9Ibg9IbY8IbQ7ILY8IbY8Ibg9Ibg9IbE7ILM7IKw5H7M7IKg3Hqw5H6U3 - Hqw5H6M2HaA1HZQxG5cyG5YyG5EwGpQxG5cyG5IwGo0uGZQxG5EwGpQxG5YyG5QxG5YyG5kyG540HLY8 - IdFFJd1bPuBrUN9kSNpLK7M7IJkyG5kyG6A1HY8wGoYsGIUsGIMsGIUsGIAqF3wpFnwpFnwpFnooFnko - FnkoFnooFnkoFnUnFXkoFnkoFnQnFXQnFXAlFG8lFG8lFGsjE2kjE2kjE2gjE2YhEmMhEmQhEmMhEl8f - EV4fEV8fEV4fEVgdEFcdEFcdEFMcD1IbD04aDlIbD04aDk0aDk0aDksZDkkYDUcYDUQWDEQWDEQWDEIW - DEIWDEEWDEEWDEEWDD0UCz0UCzoUCzoUCzYSCjYSCjUSCjUSCjMRCTMRCTEQCTMRCTMRCTAQCTAQCSwP - CCwPCCwPCCwPCCsOCCkOCCkOCCkOCCUNByUNByQMByILBiALBiILBiALBh8LBh8LBh8LBh8LBhsJBRsJ - BRsJBRoJBRoJBRgJBRoJBRgJBRgJBRYHBBYHBKelpP////////////////////////////////////// - /6alpA4FAw4FAwoDAgoDAgoDAgoDAgoDAgkDAgcDAgUCAQMBAQMBAQUCAQMBAQMBAQIBAQUCAQMBAQUC - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9FFJc5EJcxDJM5EJdFFJc9EJclC - JMlCJMdCJMVBI85EJdZGJs9EJdVGJs9EJcpDJM9EJcpDJMlCJMRBI8JAI8A/IsA/IrY8Ibg9Ibk9Ibk9 - Ib4/IrY8IbQ7ILY8IbY8IbQ7ILE7ILQ7ILk9IbQ7IKw5H6w5H6w5H6g3Hqc3Hqw5H6c3HqI2HZQxG5Iw - GpQxG5YyG5QxG5cyG5YyG5IwGo0uGY8wGo8wGo8wGpYyG5szHJcyG5EwGpIwGqA1HcRBI9xVN99kSN1b - Ps5EJaM2HZIwGpcyG6I2HZIwGoErF4ErF4ErF4AqF4ErF3ooFnkoFnooFnkoFnkoFnkoFnkoFncnFXUn - FXQnFXUnFXQnFW8lFG8lFG0kFGsjE2kjE2gjE2kjE2gjE2gjE2QhEmEhEmMhEl8fEVwfEV4fEVwfEVcd - EFcdEFcdEFcdEFAbD1AbD04aDk4aDk0aDk4aDk4aDksZDkcYDUYYDUYYDUYYDUYYDUQWDEIWDD8VDEIW - DD8VDD8VDDoUCzgSCjYSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTEQCTEQCTAQCSwPCCwPCC4QCSsOCCkO - CCkOCCcNBycNByUNByUNByUNByQMByALBiILBiALBiALBh8LBh8LBh0KBh0KBh8LBhsJBRgJBRoJBRoJ - BRgJBRgJBRgJBRoJBRoJBaelpP///////////////////////////////////////6alpA4FAw8FAw4F - Aw4FAwoDAgwFAwoDAgkDAgcDAgUCAQUCAQMBAQMBAQMBAQMBAQIBAQUCAQMBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9VGJtZGJsxDJM9EJdNGJtNGJsxDJMlCJMdCJMVBI8JA - I8pDJNNGJspDJMlCJMRBI8lCJM5EJcxDJMRBI8JAI70/IsA/Irs9IbQ7ILg9Ibg9Ibs9Ibg9IbY8IbY8 - Ibk9IbY8IbY8IbQ7ILE7ILE7IKg3Hqg3Hqo4HqU3HqU3HqA1HaA1HZkyG5QxG48wGo8wGpQxG5YyG5Yy - G40uGY0uGYouGY8wGpEwGpEwGpEwGpYyG5IwGo0uGYYsGJszHLE7INlIJ91cP9pOLrY8IZkyG4ouGZEw - GpszHI0uGYErF4ErF4AqF4AqF4AqF3wpFnooFnkoFnkoFnooFncnFXcnFXUnFXcnFXUnFXQnFXQnFXAl - FG8lFG0kFG8lFGsjE2sjE2YhEmYhEmYhEmQhEmEhEmEhEl8fEV8fEV4fEVgdEFcdEFUcD1cdEFcdEFIb - D1IbD1AbD1AbD00aDk4aDksZDkkYDUcYDUYYDUcYDUQWDEIWDEIWDEEWDD0UCz8VDD0UCz0UCz0UCzwU - CzgSCjYSCjUSCjUSCjMRCTMRCTEQCTEQCTAQCTAQCTAQCSwPCCwPCCwPCCkOCCkOCCkOCCUNBycNByUN - ByUNByQMByUNByQMByALBiALBiALBiILBh8LBh0KBhsJBRsJBRsJBRoJBRgJBRgJBRgJBRYHBBYHBBgJ - BRYHBKelpP///////////////////////////////////////6alpA4FAwwFAwwFAwwFAwoDAgwFAwoD - AgcDAgUCAQUCAQMBAQMBAQUCAQIBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9VGJtFFJc5EJcpDJNNGJsxDJMxDJMVBI8lCJMRBI8JAI8lCJM9EJc5EJc5E - JclCJMdCJM9EJc9EJcVBI7s9Ibk9Ib0/Irs9Ib0/Irk9Ibg9IbQ7ILg9IbE7ILY8IbM7ILM7IL0/IrQ7 - IK86H6g3HqA1Hao4HqU3HqU3HqI2HaM2HZ40HJcyG5YyG48wGpIwGpQxG5QxG5cyG5IwGpEwGoYsGI8w - Go8wGosuGZEwGpkyG5YyG4YsGIgtGIouGZszHMA/IthHJsVBI6I2HY0uGYUsGIsuGZ40HJIwGoErF4Ms - GIAqF3wpFnooFnkoFnkoFnooFnkoFnooFnUnFXUnFXUnFXQnFXImFXImFXImFXQnFW0kFG0kFG0kFGkj - E2kjE2sjE2YhEmYhEmYhEmQhEmMhEl8fEWEhElwfEVgdEFcdEFcdEFcdEFcdEFAbD1IbD1AbD04aDk0a - DksZDksZDkcYDUcYDUcYDUYYDUQWDEQWDEIWDD8VDD0UCz8VDD0UCzoUCzwUCzoUCzgSCjoUCzYSCjUS - CjYSCjUSCjMRCTEQCTAQCTAQCS4QCSwPCC4QCSsOCCsOCCsOCCcNBycNBycNByUNByUNByQMByQMByQM - ByILBiALBiALBiALBh0KBh0KBhsJBRsJBRsJBRoJBRgJBRoJBRoJBRgJBRgJBRYHBBYHBKelpP////// - /////////////////////////////////6alpA8FAwwFAwwFAwoDAgwFAwoDAgkDAgUCAQMBAQUCAQUC - AQUCAQUCAQIBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAIBAQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJtNGJtFFJc9EJcxDJMxDJM5EJcpDJMdCJMJAI8A/IsdCJMVBI8lCJMVBI8pDJMJAI8dCJMdC - JMdCJMdCJL0/Ir0/Ir4/Irg9IbY8Ibg9IbQ7ILg9Ibs9Ibs9IbQ7ILM7ILg9Ia05H6g3HqM2HaM2Hag3 - Hqg3Hqc3Hqc3HqA1HZkyG48wGpQxG4suGZIwGpEwGpYyG5EwGo8wGo0uGYouGYouGYgtGIYsGJIwGpIw - GpIwGosuGYYsGIYsGIouGZ40HLQ7IKo4HpIwGoYsGIMsGIMsGJszHJYyG4AqF3ooFnkoFnooFnooFnko - FnkoFnkoFncnFXUnFXcnFXcnFXImFXAlFHAlFHImFW8lFHImFW0kFG8lFGsjE2sjE2YhEmYhEmYhEmgj - E2QhEmMhEl8fEV4fEV4fEVoeEFcdEFgdEFgdEFcdEFcdEFMcD1IbD04aDk0aDksZDkkYDUsZDkkYDUcY - DUcYDUYYDUQWDEIWDEIWDEEWDD8VDEIWDD8VDDwUCz0UCzoUCzwUCzoUCzgSCjUSCjMRCTMRCTMRCTEQ - CTEQCS4QCS4QCSwPCCsOCCsOCCkOCCsOCCkOCCcNByUNByQMByUNByUNByUNByILBiILBiILBh0KBh8L - Bh0KBh0KBhsJBRoJBRoJBRoJBRgJBRoJBRsJBRoJBRYHBBgJBRYHBKelpP////////////////////// - /////////////////6alpA8FAwwFAwwFAwoDAgwFAwoDAgcDAgUCAQMBAQUCAQUCAQUCAQUCAQMBAQUC - AQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9ZGJs9EJcxD - JNFFJdNGJs5EJc5EJcxDJMlCJMVBI8RBI8A/IsJAI8lCJMlCJMdCJMdCJMdCJMA/Ir0/IsVBI8VBI74/ - Ir0/Ir0/Irk9IbQ7ILs9IbQ7IL0/Irg9IbM7IKw5H6w5H6w5H605H6I2HaU3HqU3HqU3HqI2HaU3Hpcy - G5QxG5cyG48wGo0uGY8wGpEwGpIwGo0uGYouGYgtGIUsGIgtGIYsGIYsGIsuGZEwGo0uGYouGYErF4Er - F4ErF4UsGJIwGo0uGYouGYErF4ErF4AqF5YyG5QxG4ErF3ooFnooFnooFnooFnooFnooFnkoFnkoFncn - FXkoFnkoFnQnFXImFXAlFHAlFHAlFHImFXQnFWsjE2kjE2kjE2kjE2gjE2YhEmMhEmMhEmMhEl4fEV4f - EV4fEVcdEFcdEFgdEFcdEFIbD1McD04aDlAbD1AbD1AbD00aDk4aDkkYDUsZDkkYDUcYDUYYDUQWDEQW - DEIWDEIWDEEWDD0UCzwUCzwUCzwUCzoUCzgSCjgSCjYSCjYSCjMRCTMRCTEQCTEQCTMRCTAQCTAQCS4Q - CSwPCCkOCCkOCCcNBycNBycNByUNByQMByQMByQMByQMByILBiALBiALBh8LBiALBh8LBh8LBh0KBhsJ - BRsJBRsJBRoJBRoJBRoJBRoJBRgJBRgJBRYHBKelpP////////////////////////////////////// - /6alpBEGAw8FAwwFAwwFAwoDAgkDAgkDAgcDAgcDAgUCAQMBAQMBAQUCAQMBAQMBAQMBAQMBAQMBAQIB - AQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9FFJdVGJs9EJc5EJcxDJMlCJMxD - JMRBI8VBI8VBI8JAI70/Ir0/IsRBI8dCJMlCJMVBI8RBI8RBI8A/IsRBI8RBI8VBI74/Ir0/Irk9IbQ7 - ILQ7ILM7ILk9Ibg9Ia86H6o4Hqg3Hqc3HqU3HqI2HaA1HaU3Hqc3Hqc3HpszHJYyG5IwGo8wGosuGYou - GY0uGY0uGZIwGoouGYYsGIouGYUsGIgtGIYsGIUsGI8wGpEwGo0uGYYsGIAqF4AqF3wpFoAqF4MsGIYs - GIUsGIAqF3wpFoAqF5YyG5IwGoAqF3ooFnwpFnkoFnooFnwpFnkoFnkoFnkoFnUnFXUnFXUnFXImFXIm - FW0kFG0kFHAlFHAlFHAlFG0kFGkjE2gjE2YhEmgjE2QhEmQhEl8fEV4fEV8fEV4fEV4fEVcdEFgdEFcd - EFUcD1IbD1McD1IbD1AbD1AbD1AbD04aDk4aDksZDk0aDksZDkcYDUcYDUYYDUIWDEIWDEIWDEEWDDwU - CzwUCzwUCzwUCzgSCjYSCjYSCjYSCjMRCTMRCTYSCjMRCTMRCTEQCTMRCTAQCS4QCSwPCCkOCCsOCCcN - BycNByQMByUNByQMByILBiILBiILBiILBiALBh8LBiALBh8LBh8LBh0KBh0KBhsJBRsJBRsJBRsJBRgJ - BRoJBRgJBRgJBRgJBRgJBaelpP///////////////////////////////////////6alpBEGAw8FAw4F - AwwFAwoDAgkDAgcDAgcDAgcDAgUCAQMBAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQMBAQMBAQIBAQIBAQIB - AQIBAQIBAQAAAAIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9VGJspDJM5EJc9EJcxDJM5EJclCJMdCJMVBI8RBI8JA - I8JAI74/Irs9IcA/IsVBI8dCJMRBI8JAI74/Ir0/Ir4/Irg9Ibk9IbQ7ILE7ILE7ILM7ILE7ILE7IK86 - H6M2Hac3HqU3HqU3Hqc3Hqc3HqM2HaA1HZ40HJw0HJszHJIwGpEwGpQxG4suGY8wGoouGY0uGYYsGI8w - GoYsGIgtGIgtGIUsGIYsGIErF40uGY0uGZEwGoMsGHwpFn4qF34qF3koFn4qF4UsGH4qF3wpFnUnFXoo - FpEwGpQxG4ErF3ooFnooFnkoFnkoFnkoFnkoFnQnFXUnFXcnFXImFXImFXAlFHQnFXAlFG8lFG0kFGsj - E28lFHAlFGkjE2gjE2YhEmQhEmQhEmMhEmQhEmEhEl4fEV4fEVwfEVgdEFoeEFcdEFcdEFcdEFUcD1Mc - D1IbD1AbD00aDk0aDk0aDk4aDksZDkcYDUYYDUQWDEYYDUIWDEQWDEEWDEEWDD0UCzwUCzoUCzoUCzoU - CzoUCzgSCjYSCjUSCjMRCTUSCjMRCTMRCTEQCTEQCTAQCS4QCSwPCCsOCCsOCCcNBycNBycNBycNByUN - ByUNByQMByQMByILBiILBiALBiILBh8LBh8LBh8LBh0KBhoJBRsJBRsJBRoJBRgJBRgJBRgJBRgJBRYH - BBYHBKelpP///////////////////////////////////////6alpBEGAxEGAw8FAwwFAwoDAgcDAgUC - AQUCAQUCAQUCAQUCAQMBAQUCAQIBAQIBAQUCAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9FFJcxDJMxDJMlCJMlCJM5EJcdCJMxDJMA/IsJAI8RBI74/Irk9Ibs9Ibs9 - Ib0/IsRBI70/Ir4/Ir0/Ir0/Ir0/IrY8IbM7IK05H686H605H7E7IK05H6c3Hqw5H6w5H6g3Hqg3HqM2 - Hac3Hpw0HKI2HZszHJkyG5IwGpEwGpEwGo8wGpEwGo0uGYouGYsuGY8wGoUsGIsuGYErF4UsGIErF4Us - GIErF4ErF4MsGIgtGI0uGYYsGH4qF3wpFnooFnkoFnooFoAqF34qF3ooFnkoFnkoFosuGZYyG4ErF3ko - FnkoFnkoFnUnFXkoFnUnFXImFXQnFXImFXQnFW8lFHAlFHImFW0kFG0kFGsjE20kFGsjE20kFGsjE2Qh - EmYhEmQhEmMhEmMhEmEhEmMhEmEhEl4fEVoeEFwfEVgdEFcdEFcdEFMcD1McD1IbD1IbD1IbD04aDk4a - Dk0aDk0aDksZDkkYDUcYDUQWDEYYDUIWDEIWDD8VDEEWDD8VDD8VDD0UCzoUCzoUCzoUCzYSCjYSCjYS - CjMRCTMRCTMRCTMRCTEQCTEQCS4QCSwPCC4QCSsOCCkOCCkOCCcNBycNBykOCCUNBycNByUNByILBiIL - BiALBiALBh8LBh8LBh8LBh0KBhsJBRsJBR0KBhoJBRoJBRoJBRYHBBgJBRYHBBYHBBgJBaelpP////// - /////////////////////////////////6alpBEGAxEGAw8FAwwFAwkDAgcDAgcDAgcDAgUCAQUCAQUC - AQUCAQMBAQUCAQIBAQMBAQUCAQMBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9hHJthHJtVGJspDJMdCJMdCJMVBI8VBI8RBI8lCJMJAI74/Ir0/IsRBI7g9IbQ7ILg9IbY8IcA/ - Ir0/Ir0/IrY8IbM7ILg9IbY8IbE7ILE7IK05H686H6w5H6o4Hq05H540HKc3Hqc3HqU3Hpw0HJw0HJQx - G5EwGo0uGY8wGo8wGo0uGYouGY8wGogtGI0uGYouGYMsGIouGYYsGIErF4ErF34qF3wpFoErF34qF4su - GYYsGIAqF3wpFn4qF3wpFnooFnooFoErF4ErF34qF3koFnooFoouGZQxG4ErF3koFnkoFnUnFXcnFXko - FnkoFncnFXQnFXAlFHQnFXAlFGsjE3AlFG8lFHAlFGsjE2sjE2kjE2sjE20kFGYhEmYhEmYhEmEhEl8f - EWEhEl4fEV8fEV4fEVgdEFgdEFcdEFIbD1UcD1McD1McD1IbD1IbD1AbD00aDk0aDk4aDk0aDkkYDUkY - DUQWDEQWDEcYDUQWDEIWDD8VDEEWDD0UCz0UCz0UCzoUCzgSCjYSCjUSCjYSCjUSCjMRCTEQCTEQCTEQ - CTEQCTEQCTAQCS4QCS4QCSkOCCwPCCsOCCsOCCkOCCcNByQMByUNByQMByILBiALBh8LBh8LBh0KBh8L - Bh8LBh0KBhsJBRsJBRsJBRgJBRoJBRgJBRYHBBgJBRYHBBgJBRgJBaelpP////////////////////// - /////////////////6alpBEGAxEGAxEGAw4FAwcDAgcDAgUCAQUCAQUCAQMBAQMBAQMBAQMBAQMBAQIB - AQIBAQMBAQMBAQAAAAAAAAIBAQAAAAIBAQAAAAAAAAAAAAAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9VGJtFFJc5E - JcpDJM5EJclCJMVBI8RBI8JAI8VBI8JAI70/Ir4/Ir0/Irk9IbQ7ILM7ILQ7ILQ7ILg9Ibs9IbQ7ILg9 - Ib0/Irg9Iaw5H686H6w5H6w5H6w5H6U3HqM2HaM2HaM2HaM2HZw0HJYyG40uGY0uGY0uGYouGY0uGY0u - GYYsGIYsGIouGYYsGIYsGIAqF4UsGIMsGIErF4gtGH4qF4AqF34qF3koFoErF48wGogtGIUsGHooFnwp - FnkoFnwpFnooFoErF4AqF3ooFnwpFnooFoUsGJQxG4UsGHkoFnkoFnkoFnUnFXcnFXcnFXQnFXQnFXIm - FXAlFG8lFGsjE3AlFG8lFG0kFGkjE2kjE2gjE2sjE2sjE2gjE2QhEmEhEmEhEl8fEWMhEl4fEV4fEVwf - EVgdEFcdEFUcD1McD1McD1McD1McD1McD1IbD04aDk4aDk0aDk0aDkkYDUkYDUkYDUcYDUQWDEYYDUQW - DEIWDEEWDEEWDD0UCz0UCz0UCz0UCzoUCzgSCjYSCjUSCjUSCjUSCjMRCTMRCTEQCTEQCTAQCTAQCS4Q - CS4QCSsOCCsOCCkOCCkOCCUNByQMByUNByILBiILBiILBiILBiALBiILBh8LBh0KBhsJBRsJBR0KBhsJ - BR0KBhoJBRoJBRgJBRoJBRgJBRYHBBgJBRQHBKelpP////////////////////////////////////// - /6alpBEGAxEGAw8FAwwFAwkDAgcDAgUCAQUCAQUCAQUCAQcDAgUCAQUCAQMBAQIBAQMBAQIBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9NGJs5EJcxDJNFFJcxDJMdCJMA/ - IsJAI8JAI8RBI8A/Ir4/IsJAI7k9IbY8IbM7ILY8Ia86H7Q7IKw5H686H7Q7ILE7ILM7IK05H6w5H6w5 - H6g3Hqo4HqM2Hag3HqI2Hac3HqM2HZYyG5IwGo0uGZQxG4gtGIouGY0uGYsuGYouGYouGYYsGIUsGIAq - F4ErF4MsGHwpFnwpFoAqF4AqF4UsGIErF4ErF3ooFoMsGIYsGI0uGYgtGHwpFn4qF3koFn4qF3ooFoAq - F3koFnwpFnkoFnooFoMsGJIwGogtGHkoFnkoFnkoFnImFXQnFXUnFXAlFHImFW0kFG8lFG8lFG0kFG8l - FG8lFG0kFG0kFG8lFGgjE2gjE2kjE20kFGgjE2MhEmMhEl8fEV8fEV4fEVoeEFoeEFoeEFcdEFcdEFcd - EFMcD1IbD1McD1IbD1IbD04aDk4aDk4aDksZDkkYDUsZDksZDkkYDUIWDEYYDUIWDEEWDEEWDD0UCzwU - Cz8VDD0UCz0UCzoUCzgSCjYSCjYSCjUSCjUSCjUSCjMRCTMRCS4QCTAQCTAQCS4QCS4QCSkOCCkOCCkO - CCsOCCcNByUNByUNByQMByILBiILBiILBiALBiILBh8LBh0KBh0KBhsJBR0KBhsJBR0KBh0KBhoJBRoJ - BRoJBRsJBRoJBRgJBRYHBKelpP///////////////////////////////////////6alpBEGAxEGAw8F - AwwFAwkDAgcDAgkDAgUCAQUCAQUCAQMBAQMBAQUCAQMBAQMBAQUCAQIBAQAAAAAAAAIBAQIBAQAAAAAA - AAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9VGJtNGJs9EJc9EJclCJMlCJMVBI8A/IsRBI8RBI8RB - I8JAI70/Irg9IbY8IbY8Ia05H7E7IKc3Hqg3Hq05H605H6U3HqU3Hq86H6g3Hp40HKI2HaI2HaA1HZw0 - HJszHJYyG5kyG4suGY8wGo0uGYsuGY0uGYsuGY0uGYYsGIgtGIgtGIouGYErF4MsGIErF3koFn4qF4Aq - F4ErF4AqF4AqF34qF4AqF4AqF4AqF4YsGIYsGIouGYAqF3wpFnkoFnwpFnwpFoMsGHooFn4qF3ooFnoo - FoUsGJEwGoUsGHkoFncnFXQnFXQnFXQnFXkoFnAlFHAlFHAlFHImFXAlFG0kFG0kFGsjE2kjE2YhEmYh - EmYhEmgjE2gjE2YhEmYhEmQhEmEhEmEhEmEhEloeEFoeEFoeEFwfEVcdEFgdEFcdEFMcD1McD1IbD04a - DlAbD1AbD04aDk4aDk0aDkkYDUcYDUkYDUcYDUcYDUcYDUQWDEIWDEEWDD8VDD8VDD0UCzwUCzwUCzoU - CzgSCjgSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTEQCTAQCTAQCTAQCS4QCSwPCCkOCCsOCCcNByUNBycN - ByUNByQMByQMByQMByILBiALBh8LBh8LBh0KBh0KBhsJBR0KBhsJBR0KBhsJBRoJBRoJBRgJBRgJBRgJ - BRgJBaelpP///////////////////////////////////////6alpBEGAxEGAw4FAwoDAgkDAgoDAgcD - AgUCAQUCAQUCAQMBAQUCAQUCAQUCAQMBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAIBAQIBAQAA - AAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9ZGJtVGJspDJM5EJcxDJMlCJMRBI8A/IsA/IsA/Irs9Ib0/Ir0/Irs9Ibg9 - IbY8Ia86H7E7ILE7IKg3Hqo4Hqc3HqU3HqU3HqA1HaA1HaA1HZw0HJszHJszHJQxG5IwGpQxG540HI8w - GosuGYsuGY0uGYouGYouGYYsGIsuGYgtGIgtGIAqF4AqF4ErF3ooFn4qF3wpFnwpFoMsGIMsGHooFnko - FnkoFnkoFn4qF4MsGIouGY0uGXooFnkoFnUnFXkoFnkoFn4qF3koFncnFXkoFncnFYMsGI8wGoouGXko - FnQnFXUnFXQnFXUnFXcnFXQnFXQnFW8lFHAlFHAlFGsjE20kFGgjE2kjE2YhEmYhEmYhEmYhEmQhEmYh - EmkjE2QhEmEhEmEhEl4fEVwfEV4fEVoeEFgdEFcdEFcdEFgdEFMcD1McD1IbD1AbD1McD1IbD04aDlAb - D00aDkcYDUcYDUcYDUcYDUYYDUYYDUQWDEEWDEEWDD8VDD8VDD0UCzwUCzgSCjoUCzoUCzYSCjUSCjMR - CTUSCjMRCTMRCTEQCTAQCTAQCS4QCS4QCS4QCSwPCCwPCCkOCCsOCCcNByUNByUNByQMByQMByQMByIL - BiILBiALBiALBh8LBh0KBh0KBh0KBh0KBhsJBRsJBRgJBRoJBRoJBRoJBRgJBRYHBBYHBKelpP////// - /////////////////////////////////6alpBEGAw8FAwwFAwoDAgoDAgkDAgcDAgcDAgUCAQUCAQMB - AQMBAQUCAQMBAQMBAQIBAQIBAQIBAQAAAAMBAQAAAAIBAQAAAAMBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJs9EJc5EJdVGJs9EJcRBI8RBI8JAI74/Ir4/Irs9Ibs9Ibk9Ib0/Irk9IbQ7ILE7ILQ7IK05 - H605H6o4Hqo4HqU3HqA1HZw0HKM2HZszHJszHJkyG5YyG5kyG5YyG5IwGpcyG5EwGpEwGo8wGpIwGo0u - GYUsGIYsGIYsGI8wGoouGYErF3koFoAqF4ErF4AqF3ooFn4qF4AqF4MsGIMsGHooFnkoFoErF34qF4Us - GIgtGIsuGXcnFXkoFnkoFnkoFnkoFnkoFnkoFnwpFnkoFnkoFnwpFo8wGosuGXooFncnFXImFXUnFXcn - FXQnFXUnFXQnFXAlFGsjE3AlFGsjE20kFG8lFGkjE2sjE2sjE2YhEmYhEmQhEmYhEmQhEmQhEmEhEl8f - EVoeEFwfEV8fEVwfEVgdEFcdEFUcD1cdEFUcD1IbD1IbD1McD1McD1IbD04aDk0aDksZDkkYDUkYDUsZ - DkkYDUcYDUcYDUQWDD8VDEEWDD8VDD0UCz8VDD0UCzoUCzwUCzwUCzYSCjUSCjMRCTMRCTMRCTMRCTEQ - CTAQCS4QCSsOCC4QCS4QCSwPCCcNBysOCCkOCCkOCCcNBycNByUNByUNByQMByILBiALBiALBh8LBh8L - BiILBh8LBh0KBhsJBR0KBhsJBRoJBRoJBRgJBRgJBRoJBRgJBRYHBKilpf////////////////////// - /////////////////6elpA8FAw4FAwwFAwoDAgoDAgoDAgcDAgkDAgcDAgUCAQUCAQMBAQMBAQUCAQMB - AQMBAQIBAQMBAQAAAAMBAQIBAQAAAAAAAAAAAAIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9FFJc9EJdFF - Jc5EJc9EJcRBI8lCJMA/Ir0/Ir4/Irs9IbY8Ibg9IbQ7IL0/Irg9IbQ7ILE7IK05H686H605H6g3HqA1 - HZ40HJszHJ40HJszHJszHJszHJYyG5QxG5YyG40uGZcyG5IwGpIwGpEwGo8wGpEwGo0uGYsuGYMsGIYs - GIErF3wpFoAqF3ooFnwpFn4qF34qF4AqF34qF4ErF4AqF3cnFXooFnooFnwpFoYsGIsuGYgtGH4qF3cn - FXkoFnkoFnkoFnkoFncnFXooFnkoFnkoFnkoFoouGYYsGHUnFXcnFXQnFXImFXAlFG8lFHQnFXAlFG8l - FHAlFG8lFGsjE20kFGkjE2YhEmYhEmgjE2gjE2gjE2QhEmQhEmMhEmYhEmQhEl8fEVoeEFoeEFoeEFgd - EFoeEFgdEFcdEFcdEFUcD1McD1McD1IbD04aDk4aDlAbD04aDk4aDksZDkkYDUsZDksZDkcYDUYYDUQW - DEEWDEIWDEEWDD8VDD8VDD8VDDwUCzgSCjYSCjUSCjYSCjMRCTUSCjUSCjMRCTAQCTEQCTAQCSwPCCwP - CCwPCCsOCCkOCCsOCCcNBycNBykOCCkOCCUNByQMByQMByILBiALBiALBiALBh8LBh8LBh8LBh0KBhsJ - BRsJBRsJBRoJBRoJBRgJBRgJBRgJBRgJBRQHBKilpf////////////////////////////////////// - /6alpA8FAwwFAwwFAwoDAgoDAgwFAwkDAgcDAgUCAQUCAQUCAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQAA - AAIBAQAAAAAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9VGJtZGJs5EJdVGJslCJMlCJMxD - JMVBI8VBI70/Irk9Ibs9Ibk9IbY8Ibg9IbM7ILM7ILQ7IKw5H6w5H686H6g3HqU3Hqc3HqA1HZw0HJsz - HJszHJkyG5kyG5IwGpYyG5IwGpcyG48wGo8wGosuGYouGYsuGYouGYYsGIMsGIErF4AqF3wpFn4qF34q - F34qF3wpFoAqF34qF3koFoAqF4AqF34qF3ooFnooFnooFnwpFo8wGo0uGXooFnUnFXcnFXkoFnkoFncn - FXQnFXcnFXooFnUnFXkoFosuGYouGXooFncnFW8lFHImFXQnFXImFXImFXcnFXAlFGsjE2sjE20kFG8l - FGkjE2YhEmgjE2gjE2gjE2gjE2kjE2MhEmMhEmQhEmMhEmEhEl4fEV8fEVoeEFoeEFgdEFcdEFcdEFcd - EFUcD1UcD1UcD1McD04aDlIbD1AbD00aDk0aDk0aDkkYDUkYDUYYDUkYDUYYDUIWDEEWDEYYDUIWDEIW - DD8VDD8VDDwUCzgSCjYSCjYSCjUSCjMRCTMRCTMRCTMRCTMRCTAQCTAQCTEQCSsOCC4QCSwPCCkOCCsO - CCcNBycNBykOCCcNBycNByILBiILBiILBh8LBiALBiALBh8LBh0KBh0KBh8LBh0KBhsJBRsJBRsJBRoJ - BRgJBRYHBBgJBRgJBRYHBKelpP///////////////////////////////////////6alpA4FAwwFAwwF - AwoDAgwFAwwFAwoDAgcDAgUCAQMBAQMBAQcDAgMBAQUCAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////89EJdVGJtFFJc9EJcpDJMpDJMRBI8dCJL4/IsJAI70/ - Ir0/Irk9IbY8Ibg9IbM7IK86H7M7IKo4Hqo4Hqo4Hqg3Hqg3HqI2HaA1HaA1HZ40HJw0HJQxG5YyG5Qx - G5cyG5IwGpkyG48wGo8wGoouGY8wGo0uGYUsGIMsGIMsGIAqF4ErF4AqF4AqF4AqF4AqF4AqF34qF3wp - FoMsGIAqF4AqF3cnFXkoFnUnFXkoFoAqF4suGY8wGoErF3koFnQnFXkoFnkoFnwpFnkoFnkoFnkoFnko - FncnFYUsGIouGXooFnUnFXImFW8lFHAlFHQnFXQnFXUnFW0kFG0kFG0kFGsjE2sjE2kjE2gjE2gjE2gj - E2sjE2QhEmYhEmQhEmMhEl8fEWEhEmMhEl4fEVoeEFcdEFcdEFoeEFcdEFUcD1cdEFMcD1McD1McD1Mc - D1AbD1AbD1AbD1AbD04aDk0aDkkYDUcYDUYYDUcYDUcYDUQWDEIWDEIWDEEWDEEWDD0UCz8VDDwUCzoU - CzgSCjgSCjYSCjYSCjUSCjUSCjMRCTEQCTAQCS4QCTEQCS4QCSwPCCsOCCkOCCkOCCcNByUNByUNByQM - ByUNByUNByUNByILBiALBiALBh8LBh8LBh0KBh0KBh0KBhsJBRsJBRsJBRsJBRoJBRoJBRoJBRgJBRYH - BBYHBKelpP///////////////////////////////////////6alpAwFAwwFAw4FAw4FAwwFAwoDAgwF - AwkDAgcDAgMBAQMBAQcDAgMBAQMBAQMBAQMBAQMBAQMBAQAAAAIBAQIBAQIBAQAAAAIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9ZGJtFFJdNGJtNGJsxDJMlCJMJAI8JAI8A/IsA/Irs9Ib4/Irg9Ibs9IbE7 - ILE7ILE7IK86H605H6o4Hqg3Hqo4HqU3Hqg3Hp40HJ40HJ40HJ40HJw0HJIwGpYyG5cyG5EwGpIwGpQx - G48wGogtGIsuGYYsGIUsGIYsGIMsGIMsGIErF34qF34qF4AqF4ErF4ErF34qF3ooFoAqF4AqF3wpFnoo - FnkoFnwpFnkoFnwpFogtGI0uGYMsGHooFnooFncnFXkoFnooFnkoFnwpFnUnFXcnFXkoFoMsGIUsGHwp - FnUnFXQnFXUnFXAlFG8lFHcnFXQnFXImFW0kFG0kFGsjE2sjE20kFGsjE2kjE2YhEmkjE2gjE2QhEmQh - EmQhEl8fEV4fEWEhEmEhElwfEVoeEFcdEFoeEFcdEFcdEFcdEFUcD1McD1McD1IbD1AbD1IbD1IbD1Ab - D00aDk0aDkkYDUcYDUcYDUcYDUcYDUYYDUIWDEEWDD8VDD0UCz8VDDwUCzgSCjgSCjgSCjgSCjYSCjUS - CjUSCjMRCTEQCTAQCTEQCS4QCTAQCS4QCSsOCCkOCCsOCCkOCCkOCCUNBycNByUNByQMByUNByQMByQM - ByILBiILBiALBh8LBh0KBh0KBhsJBRsJBR0KBhsJBRsJBRoJBRgJBRgJBRgJBRYHBBYHBKelpP////// - /////////////////////////////////6alpA4FAw4FAwwFAwoDAgoDAgwFAwoDAgkDAgUCAQUCAQUC - AQUCAQUCAQUCAQMBAQMBAQMBAQMBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9NGJtVGJsxDJNNGJs9EJclCJMpDJMRBI8lCJMA/Ir4/Irs9Ib0/Irk9IbE7IK86H605H686H7E7 - IKc3Hqc3Hqo4HqU3Hqc3HqU3HqI2HZszHJszHJkyG5IwGpEwGo8wGpIwGpEwGo8wGosuGYYsGIYsGIYs - GIYsGIgtGIgtGIErF4MsGIMsGIUsGH4qF3ooFoErF4ErF3ooFnwpFoMsGIMsGIErF4AqF3wpFnooFnoo - FoouGY8wGoouGX4qF3wpFnkoFnooFn4qF3ooFnkoFncnFXUnFXkoFoMsGIUsGH4qF3UnFXUnFXImFXQn - FXAlFHQnFXQnFXImFXAlFHQnFW0kFGkjE2sjE2gjE2gjE2YhEmMhEmQhEmQhEmMhEmMhEmEhEl8fEWEh - El4fEVwfEVoeEFcdEFgdEFcdEFcdEFcdEFUcD1McD1McD1McD1McD1IbD1IbD04aDksZDk0aDkkYDUcY - DUcYDUkYDUYYDUQWDEEWDEEWDEIWDD8VDD8VDDwUCzwUCzoUCzYSCjYSCjYSCjUSCjMRCTEQCTAQCTEQ - CTMRCS4QCSwPCDAQCS4QCSsOCCkOCCcNBysOCCcNBycNByUNByUNByQMByILBiILBiILBiILBiALBh8L - Bh0KBh0KBhsJBR8LBh0KBh0KBhsJBRoJBRgJBRoJBRgJBRYHBBYHBKelpP////////////////////// - /////////////////6alpA4FAwoDAgoDAgwFAwwFAwwFAwoDAgoDAgcDAgUCAQMBAQMBAQMBAQMBAQMB - AQMBAQIBAQIBAQIBAQMBAQAAAAIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9hHJtZGJtVG - JtFFJc9EJcdCJMpDJMlCJMlCJMJAI8JAI70/Irk9IbQ7ILQ7ILE7ILM7ILM7IK86H6o4HqM2HaA1HaI2 - Hao4HpszHJYyG5cyG5kyG5cyG5YyG5cyG48wGpQxG48wGosuGYsuGYgtGIouGYsuGYUsGIMsGIMsGIUs - GIMsGIgtGIAqF3wpFoErF4MsGIErF3koFnooFnooFoUsGHwpFoAqF3ooFnkoFnkoFogtGJEwGogtGHko - FnwpFnooFnooFn4qF3wpFnkoFnkoFnkoFnUnFYMsGIYsGH4qF3QnFXImFXUnFXQnFXUnFW8lFHImFXcn - FXAlFG0kFG0kFGkjE2gjE2YhEmYhEmgjE2QhEmQhEmMhEl8fEV8fEV4fEV8fEWEhEl4fEVwfEVoeEFcd - EFgdEFgdEFcdEFcdEFUcD1IbD1McD1McD1IbD1AbD04aDk4aDk0aDkkYDUcYDUcYDUcYDUkYDUQWDEQW - DEEWDEIWDEIWDD8VDD0UCz0UCzwUCzgSCjoUCzYSCjUSCjMRCTMRCTMRCTMRCTMRCTEQCS4QCSwPCC4Q - CS4QCSwPCCkOCCsOCCsOCCkOCCUNByUNByQMByILBiILBiILBiILBiALBiILBiALBh0KBh0KBhsJBR8L - Bh0KBh0KBhoJBRoJBRgJBRgJBRgJBRYHBBYHBKelpP////////////////////////////////////// - /6alpA4FAw4FAwwFAw4FAw4FAwwFAwoDAgoDAgkDAgUCAQUCAQUCAQMBAQMBAQMBAQIBAQMBAQMBAQMB - AQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9VGJtVGJtVGJs9EJcpDJMpDJMlC - JMdCJMdCJL4/IsA/Ir0/Irk9IbY8IbQ7IK86H686H605H605H6w5H6w5H6I2HaI2HZszHKI2HZw0HJYy - G5kyG5cyG5QxG5EwGo0uGZIwGo8wGoYsGIsuGYsuGYouGYouGYgtGIMsGH4qF4AqF34qF3wpFn4qF34q - F3ooFnwpFoMsGIAqF34qF3wpFoUsGHwpFnooFnwpFnkoFnwpFoouGY8wGogtGHcnFXkoFnUnFXwpFoAq - F3koFnkoFnooFncnFXkoFnwpFoYsGIUsGHUnFXQnFXImFXImFXAlFHQnFXImFXcnFXAlFG0kFG0kFG0k - FGgjE2sjE2kjE2gjE2QhEmQhEmQhEl8fEV8fEV8fEV8fEV8fEV8fEV4fEVoeEFcdEFcdEFcdEFUcD1cd - EFcdEFMcD1UcD1IbD1IbD1IbD00aDk4aDk0aDkkYDUkYDUsZDksZDkkYDUYYDUQWDEIWDEIWDEEWDD8V - DD0UCzwUCzwUCzgSCjwUCzgSCjYSCjMRCTUSCjYSCjUSCjMRCTEQCTAQCTAQCS4QCSwPCCsOCCsOCCkO - CCkOCCcNBycNByUNByILBiILBiILBiILBiALBiALBiALBiALBh0KBhsJBRsJBR0KBh0KBhoJBRsJBRoJ - BRoJBRgJBRgJBRYHBBYHBKelpP///////////////////////////////////////6alpA4FAwwFAw4F - AwwFAwwFAwwFAwoDAgwFAwkDAgUCAQUCAQUCAQMBAQMBAQUCAQUCAQMBAQIBAQIBAQIBAQAAAAAAAAIB - AQAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9NGJthHJthHJs5EJc5EJcpDJMdCJMJAI8dCJMJAI74/ - Ir0/Ir0/IrY8Ibg9Iao4Hq05H605H686H6c3Hqo4Hqg3HqA1HZw0HJszHJszHJw0HJYyG5YyG5EwGpEw - Go0uGZEwGpEwGosuGYYsGIouGZEwGoYsGIYsGIErF4AqF3wpFoAqF4MsGHwpFnkoFoAqF4AqF34qF34q - F3wpFoMsGIErF3wpFnwpFn4qF3koFnwpFoouGY8wGosuGXkoFnkoFncnFXkoFnwpFnkoFnooFnkoFncn - FXkoFnkoFogtGIYsGHooFncnFXcnFXImFXImFXUnFXQnFXUnFXAlFG8lFG8lFGsjE2kjE2gjE2kjE2Yh - EmQhEmYhEmgjE2MhEl8fEV8fEV4fEV4fEV8fEV8fEVgdEFcdEFgdEFgdEFcdEFUcD1UcD1UcD1McD1Ab - D1AbD04aDk0aDk4aDk4aDksZDksZDk0aDksZDkcYDUQWDEIWDEIWDEIWDD8VDD0UCzwUCzoUCzoUCzgS - CjwUCzgSCjYSCjUSCjUSCjYSCjMRCTEQCTMRCTEQCS4QCTAQCSwPCCsOCCwPCCkOCCcNBycNBycNByUN - ByQMByQMByILBiILBiILBiALBiILBiALBh8LBh0KBh0KBh0KBhsJBRoJBRoJBRgJBRoJBRoJBRgJBRgJ - BRYHBKelpP///////////////////////////////////////6alpA4FAwwFAw4FAwwFAwwFAwoDAgkD - AgkDAgcDAgcDAgUCAQUCAQUCAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQAAAAAA - AAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9ZGJtZGJtVGJs9EJc9EJcxDJMlCJMVBI8RBI8RBI8JAI70/Ir0/IrY8Ibg9 - IbE7IK05H6w5H6o4Hqg3Hqg3HqU3HqA1HZ40HKI2HZw0HJQxG5kyG5szHJIwGpQxG5QxG5EwGpEwGo0u - GYsuGYouGYYsGIgtGIgtGIgtGIgtGIMsGIMsGIMsGIErF4YsGIAqF3wpFoAqF3wpFnkoFnwpFoErF34q - F4ErF3ooFoAqF3koFogtGI8wGosuGXooFnooFncnFXooFnooFnwpFnwpFnwpFnUnFXkoFnooFogtGIYs - GHcnFXcnFXQnFXImFXcnFXQnFXQnFXQnFXQnFXAlFGsjE2sjE2gjE2kjE2YhEmQhEmMhEmEhEl8fEWEh - EmEhEmMhEl4fEV4fEVwfEVwfEV4fEVgdEFgdEFcdEFcdEFcdEFMcD1McD1McD1AbD1IbD04aDk4aDk4a - Dk4aDk4aDk0aDkkYDUcYDUYYDUIWDEQWDEEWDEIWDEEWDDwUCz0UCz0UCzwUCzoUCzoUCzYSCjYSCjUS - CjUSCjUSCjMRCTEQCTMRCTMRCS4QCS4QCSsOCCkOCCsOCCsOCCkOCCsOCCkOCCUNByQMByQMByQMByIL - BiALBiALBiALBh0KBh0KBh0KBh0KBhoJBRsJBRsJBRoJBRoJBRoJBRgJBRgJBRYHBBQHBKelpP////// - /////////////////////////////////6alpA4FAwwFAwoDAgwFAwwFAwwFAwoDAgoDAgoDAgkDAgUC - AQUCAQMBAQUCAQMBAQIBAQIBAQMBAQIBAQAAAAIBAQMBAQIBAQMBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9lIJ9ZGJs9EJcxDJM5EJdFFJc5EJcVBI8lCJMA/Irs9IcA/Irs9IbY8IbE7ILE7IKw5H6w5H605 - H6w5H6U3HqM2HaI2HZw0HJw0HJszHJQxG5szHJYyG5IwGpQxG5cyG5IwGogtGI0uGY8wGosuGYYsGIYs - GI0uGYgtGIUsGIUsGIErF4UsGIUsGIErF4YsGIAqF4MsGIAqF3wpFoMsGIUsGH4qF4AqF4ErF4AqF4Er - F34qF5IwGpEwGnwpFoErF3UnFXooFnwpFnwpFnooFncnFXUnFXkoFnwpFoYsGIgtGHkoFnUnFXcnFXUn - FXAlFHUnFXkoFnUnFXAlFHImFW0kFHAlFGsjE2sjE2gjE2MhEmgjE2EhEl8fEWMhEl8fEV8fEVwfEV8f - EVwfEVwfEV4fEVoeEFoeEFgdEFgdEFcdEFUcD1cdEFIbD1IbD1IbD1IbD1AbD0sZDk0aDk4aDksZDkkY - DUYYDUQWDEIWDEYYDUEWDEIWDEEWDEEWDD8VDD0UCz0UCzoUCzgSCjUSCjgSCjYSCjYSCjUSCjMRCTMR - CTEQCTEQCS4QCS4QCSsOCCkOCCsOCCkOCCkOCCkOCCcNByUNByQMByILBiUNByUNByALBiALBiALBh8L - Bh8LBh0KBhsJBRoJBRoJBRsJBRoJBRsJBRoJBRoJBRoJBRYHBBQHBKelpP////////////////////// - /////////////////6alpAwFAwwFAwwFAwoDAgwFAwkDAgoDAgkDAgcDAgkDAgcDAgUCAQMBAQMBAQMB - AQIBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9ZGJtFFJdVG - JthHJsxDJM5EJcpDJMlCJMJAI74/IsJAI8A/Ir4/Irk9IbQ7IK86H7E7IKw5H7E7IKw5H6I2HaA1HaI2 - HaI2HZszHJ40HJkyG5cyG5cyG5cyG5szHJYyG48wGo0uGYsuGY8wGogtGIsuGYgtGIouGYsuGYsuGYMs - GIYsGIMsGIAqF4YsGIUsGH4qF4ErF4ErF4AqF4ErF4MsGIMsGIMsGHwpFnkoFnwpFoAqF5EwGpIwGoAq - F4AqF3koFnwpFnwpFnooFnwpFnooFnkoFnooFnkoFoUsGIsuGXooFnkoFnQnFXUnFXQnFXUnFXUnFXIm - FXUnFXAlFG0kFHAlFG8lFGsjE2gjE2gjE2QhEmQhEmQhEmQhEmMhEl8fEV8fEV8fEV8fEVwfEVwfEV4f - EVgdEFcdEFcdEFUcD1McD1UcD1IbD1IbD1AbD1AbD1AbD04aDk4aDk0aDksZDksZDkcYDUYYDUIWDEIW - DEEWDEIWDEIWDD8VDDwUCzwUCzwUCzwUCzgSCjgSCjgSCjYSCjMRCTMRCTMRCTEQCTAQCS4QCS4QCSwP - CCsOCCkOCCsOCCcNBycNBycNByUNByUNByQMByQMByQMByILBiALBiALBiALBh8LBh0KBh0KBh0KBh0K - BhoJBRoJBRoJBRoJBRoJBRgJBRgJBRYHBBQHBKelpP////////////////////////////////////// - /6alpA4FAwwFAwwFAwoDAgwFAwkDAgkDAgoDAgoDAgkDAgcDAgUCAQMBAQMBAQUCAQIBAQUCAQIBAQAA - AAAAAAAAAAAAAAIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9VGJtVGJtNGJtFFJcxDJMdCJNFF - JcdCJMJAI8JAI74/IsA/Ir4/Irg9IbQ7ILY8Iaw5H7E7IK86H6U3Hqc3HqM2HZszHJw0HJw0HKA1HaM2 - HZkyG5QxG5cyG540HJYyG5QxG5YyG48wGpIwGosuGYgtGIsuGYgtGIouGYsuGYouGYouGYgtGIUsGIAq - F4YsGIAqF4suGX4qF34qF4MsGHwpFoUsGIMsGIAqF3ooFn4qF3wpFosuGZYyG4AqF3ooFoAqF4AqF34q - F3wpFnwpFnwpFnkoFnooFn4qF4MsGIYsGHwpFncnFXcnFXQnFXImFXUnFXUnFXcnFXcnFXImFXImFW8l - FGsjE2kjE2sjE2gjE2kjE2kjE2MhEmYhEmQhEmMhEl8fEV8fEVwfEVwfEVoeEFoeEFcdEFcdEFcdEFUc - D1McD1UcD1McD1IbD04aDk4aDlAbD04aDk0aDksZDkkYDUsZDkcYDUQWDEQWDEQWDEIWDEEWDEEWDD8V - DDwUCz0UCzwUCzwUCzYSCjgSCjgSCjYSCjMRCTMRCTMRCTEQCTEQCTAQCTAQCTAQCSwPCCsOCCsOCCcN - BykOCCcNByUNByQMByUNByUNByQMByALBiALBiILBiALBh8LBiALBh0KBh0KBh0KBhsJBRsJBRoJBRoJ - BRoJBRgJBRgJBRoJBRYHBKelpP///////////////////////////////////////6alpA4FAwoDAgoD - AgoDAgwFAwoDAgoDAgoDAgkDAgoDAgkDAgUCAQUCAQUCAQMBAQIBAQUCAQIBAQAAAAIBAQAAAAAAAAIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9ZGJtNGJtVGJtNGJs5EJcpDJMRBI8RBI8VBI8dCJMJA - I8RBI7s9Ib0/Irs9IbQ7ILE7ILY8IaU3HrE7IKc3Hqc3HqM2HZw0HKI2HZw0HJszHJcyG5cyG5szHJsz - HJYyG5IwGpIwGpYyG5IwGogtGIYsGIgtGIsuGYsuGZEwGo0uGYsuGYYsGIYsGIouGYYsGIErF4UsGIUs - GIMsGIAqF4AqF4AqF4UsGIMsGHwpFn4qF34qF48wGpkyG4ouGXkoFnwpFoAqF3wpFoAqF4AqF3wpFnko - FnooFnkoFoMsGIUsGIAqF3UnFXkoFnUnFXQnFXImFXQnFXUnFXQnFXQnFXImFW0kFGkjE2sjE2sjE2Yh - EmkjE2kjE2YhEmYhEmQhEl8fEV8fEV8fEV8fEVoeEFoeEFoeEFwfEVgdEFcdEFcdEFMcD1McD1IbD1Ib - D00aDlIbD1IbD00aDksZDksZDksZDksZDkkYDUYYDUYYDUYYDUIWDD8VDEEWDEEWDDwUCz0UCz0UCzoU - CzgSCjoUCzgSCjYSCjMRCTEQCTEQCTEQCTAQCTEQCS4QCSwPCC4QCSwPCCsOCCkOCCcNBykOCCcNByQM - ByQMByQMByILBiALBiILBiILBiALBh8LBh8LBh0KBh0KBhsJBR0KBhsJBRgJBRgJBRoJBRgJBRoJBRoJ - BRYHBKelpP///////////////////////////////////////6alpA4FAwwFAwwFAwwFAwwFAwoDAgwF - AwkDAgkDAgkDAgkDAgcDAgUCAQUCAQUCAQMBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9VGJtZGJtFFJcxDJMpDJMlCJMVBI8VBI8dCJMVBI74/IsJAI8JAI7Y8Ibg9 - IbQ7ILM7IK86H6o4Hqo4HqU3HqU3HqM2HaA1HZszHJszHJ40HJszHJszHJszHJkyG5EwGpcyG5QxG5Ew - GpEwGogtGIUsGIUsGIsuGY0uGY0uGYsuGY0uGYgtGIgtGIouGYouGYYsGIgtGIMsGIMsGIAqF4MsGIEr - F4gtGIUsGIAqF4ErF4AqF5IwGpkyG4suGX4qF3wpFoAqF4ErF34qF34qF3ooFnooFnooFnkoFoMsGIou - GYUsGHkoFnkoFnkoFnkoFnkoFnUnFXkoFnQnFXImFXImFWsjE20kFGkjE2sjE2kjE2YhEmYhEmkjE2Qh - EmMhEmMhEl8fEV4fEVwfEVoeEFwfEVoeEFwfEVoeEFcdEFcdEFUcD1McD1IbD1IbD1IbD1IbD1AbD00a - DksZDk0aDksZDkkYDUYYDUcYDUYYDUQWDEIWDEEWDEEWDD8VDD0UCzoUCzoUCzgSCjgSCjgSCjgSCjUS - CjMRCTMRCTEQCTEQCTEQCTAQCS4QCSwPCCwPCCsOCCkOCCkOCCcNBycNByUNByUNByQMByILBiILBiIL - BiILBiALBiALBiALBh8LBh0KBh0KBh0KBhsJBRoJBRgJBRgJBRgJBRoJBRoJBRgJBRgJBailpf////// - /////////////////////////////////6alpA8FAw4FAwoDAgwFAwwFAwoDAgwFAwoDAgoDAgcDAgkD - AgcDAgUCAQUCAQMBAQMBAQMBAQUCAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9hHJthHJtZGJtFFJc9EJcxDJMdCJMVBI8VBI8RBI8RBI70/Ir4/Ir4/Irg9IbM7ILE7ILM7IK86 - H686H6g3Hqg3HqU3Hp40HJ40HJszHJszHJw0HKA1HZszHJszHJYyG5cyG5QxG5IwGosuGY0uGY8wGo8w - Go0uGYsuGY8wGpIwGpQxG4ouGYsuGYsuGY0uGYsuGYsuGYgtGIYsGIgtGIMsGIouGYouGYYsGIErF4Er - F4UsGI8wGpkyG48wGoAqF4AqF4UsGIAqF34qF4ErF34qF34qF3ooFnkoFoMsGIYsGIUsGHcnFXcnFXcn - FXkoFnUnFXUnFXkoFnkoFnQnFXAlFG0kFGsjE2sjE2gjE2kjE2gjE2gjE2gjE2MhEmQhEmEhEl4fEVwf - EV4fEVoeEFgdEFgdEFgdEFwfEVcdEFcdEFUcD1McD1IbD1AbD1IbD04aDk4aDk4aDksZDksZDksZDksZ - DkcYDUYYDUQWDEQWDD8VDEEWDEEWDD8VDDwUCzoUCzgSCjoUCzYSCjYSCjYSCjUSCjUSCjMRCTMRCTEQ - CTEQCS4QCS4QCS4QCSwPCCwPCCkOCCcNByUNBycNByUNByUNByQMByILBiUNByQMByALBiALBiALBh8L - Bh8LBh0KBh0KBh8LBhsJBRoJBRgJBRgJBRoJBRgJBRoJBRgJBRYHBKelpP////////////////////// - /////////////////6alpA4FAw4FAwwFAwoDAg4FAwwFAwoDAgoDAgwFAwoDAgkDAgcDAgIBAQUCAQMB - AQIBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lIJ9ZGJtFF - Jc5EJc5EJc5EJcdCJMdCJMRBI8RBI70/Irs9Ib0/Ir0/Ir0/IrQ7ILM7ILE7IK05H6g3Hqo4Hqc3HqI2 - HaU3HqA1HaA1HZ40HKM2HZw0HJw0HJkyG5szHJYyG5szHJEwGpcyG5IwGpcyG5cyG5EwGpEwGo0uGZEw - GpIwGogtGIsuGY0uGY8wGoouGY0uGY0uGYouGYsuGYgtGIgtGIYsGI8wGogtGIMsGIYsGI8wGpw0HJYy - G4MsGH4qF4ErF3wpFoAqF4AqF3ooFnooFnooFn4qF4ErF4gtGIMsGHkoFnkoFnUnFXkoFnooFnkoFnko - FncnFXkoFm8lFG8lFGsjE2sjE2kjE2sjE2kjE2gjE2YhEmMhEmQhEmMhEl4fEV8fEWEhEl8fEVoeEFgd - EFoeEFoeEFcdEFcdEFUcD1McD1IbD1McD1IbD00aDk0aDk0aDksZDk0aDkkYDUsZDkcYDUQWDEIWDEQW - DEEWDD8VDEEWDEEWDD0UCzwUCzwUCzoUCzgSCjgSCjYSCjMRCTUSCjMRCTMRCTMRCTEQCTAQCS4QCS4Q - CSsOCCsOCCsOCCsOCCcNBykOCCcNByUNByQMByILBiUNByILBiALBiALBh8LBh0KBh0KBhsJBRsJBR0K - BhsJBRsJBRsJBRoJBRoJBRgJBRYHBBYHBBYHBKilpf////////////////////////////////////// - /6alpA4FAw4FAw4FAwwFAwwFAwoDAgoDAgoDAgkDAgoDAgoDAgcDAgUCAQUCAQMBAQMBAQIBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9lIJ9VGJtVGJs5EJc5EJcpDJMlC - JMdCJMdCJMVBI8A/Irs9Ib0/Irk9Ibg9IbY8IbY8Ia86H7E7IKw5H605H6c3HqM2HaI2HaA1HaI2HaM2 - HaU3HqA1HZ40HJ40HJcyG540HJszHJQxG5IwGpcyG5kyG5YyG5IwGo8wGpEwGpYyG5IwGpIwGo8wGpEw - Go0uGYsuGYgtGIouGYouGZEwGoouGYgtGIUsGIouGYUsGIouGYouGY8wGp40HJkyG4gtGIUsGIouGYgt - GH4qF4AqF34qF3ooFnwpFnooFoUsGIgtGIYsGIAqF3koFncnFXkoFncnFXkoFnQnFXUnFXImFXImFW8l - FGsjE2kjE2kjE20kFGsjE2gjE2gjE2gjE2MhEmEhEmEhEmMhEl4fEV8fEVwfEVwfEVwfEVwfEVgdEFcd - EFcdEFUcD1UcD1McD1IbD00aDk0aDk0aDksZDkkYDUsZDksZDkYYDUQWDEYYDUQWDEIWDEIWDD8VDEEW - DD0UCzwUCzoUCzgSCjgSCjYSCjYSCjUSCjUSCjMRCTMRCTEQCTMRCTAQCS4QCTAQCSsOCCsOCCsOCCkO - CCcNBycNBycNBycNByQMByILBiILBiILBiILBh8LBiALBh8LBh0KBh8LBhsJBR0KBhsJBRsJBRoJBRoJ - BRgJBRgJBRYHBBYHBBYHBKelpP///////////////////////////////////////6alpAwFAw4FAw4F - Aw4FAwwFAwoDAgoDAgoDAgkDAgkDAgkDAgkDAgUCAQUCAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9lJKdhHJtVGJs9EJdFFJc5EJclCJMpDJMpDJMdCJMlC - JL0/Ir0/Ir0/IrY8Ibk9Ibg9IbE7ILE7IKc3Hq05H6o4HqM2Hag3Hqo4HqM2Hac3HqM2HaI2HaI2HaA1 - HaI2HZw0HJw0HJ40HJkyG5cyG5YyG5cyG5cyG5EwGpEwGo8wGpYyG5kyG48wGpQxG5QxG40uGYouGYou - GYouGYouGY0uGYouGYgtGIsuGYsuGYsuGYYsGI0uGaA1HaI2HYsuGY0uGZcyG48wGoYsGIUsGIErF4Ms - GH4qF3wpFoYsGIouGYUsGHwpFnooFnkoFnooFnkoFnImFXUnFXcnFXUnFXQnFW8lFG0kFG0kFGkjE2sj - E2sjE2sjE2gjE2gjE2MhEmMhEmQhEmEhEl8fEVwfEV8fEVoeEFoeEFgdEFgdEFcdEFcdEFoeEFMcD1Ab - D1AbD04aDk0aDk4aDksZDkcYDUcYDUsZDkcYDUQWDEIWDEQWDEIWDEEWDEEWDEEWDD0UCz0UCzwUCzwU - CzoUCzUSCjUSCjYSCjMRCTUSCjMRCTMRCTMRCS4QCSwPCDAQCSwPCCwPCCwPCCkOCCcNByUNByUNBycN - ByUNByILBiILBiILBiILBh8LBiALBiALBh0KBhsJBRsJBR8LBhsJBRoJBRoJBRoJBRsJBRgJBRgJBRYH - BBgJBaelpP///////////////////////////////////////6alpA4FAw4FAwwFAwwFAwoDAgkDAgkD - AgoDAgoDAgkDAgcDAgoDAgcDAgMBAQMBAQIBAQMBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQIBAQAAAAIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9lIJ9VGJtlIJ9hHJtFFJcxDJMlCJMRBI8pDJMJAI8RBI8A/Ir0/Irg9Ibg9 - Ibg9IbQ7ILg9IbE7ILE7ILM7IK05H6c3Hqc3Hqc3HqU3Hqg3HqU3Hqc3Hp40HKI2HaI2HZszHJ40HJsz - HJYyG5cyG5szHJQxG5cyG5IwGo0uGZIwGo8wGpcyG5QxG5EwGo8wGo8wGpQxG40uGY0uGYsuGZIwGoYs - GIgtGI0uGYsuGY0uGYgtGIsuGZszHKU3HpEwGp40HLs9IaI2HY0uGYgtGIUsGIUsGH4qF4MsGIYsGIgt - GIgtGIUsGH4qF3ooFnkoFnkoFncnFXUnFXQnFXQnFXQnFXImFXImFW0kFG0kFGsjE2sjE2gjE2sjE2Yh - EmQhEmYhEmMhEmQhEl8fEV8fEV4fEVoeEFoeEFcdEFcdEFgdEFcdEFcdEFMcD1IbD1IbD1AbD04aDk4a - Dk0aDksZDkkYDUsZDkcYDUcYDUQWDEIWDEIWDEEWDD8VDD8VDD0UCz0UCzwUCzwUCzwUCzoUCzYSCjYS - CjMRCTMRCTMRCTAQCTEQCS4QCS4QCTAQCSwPCCsOCCwPCCkOCCUNBycNBycNByUNByQMByILBiILBiQM - ByALBh0KBiALBiALBh8LBh8LBh0KBh0KBhoJBRsJBRoJBRoJBRoJBRoJBRYHBBQHBBYHBKelpP////// - /////////////////////////////////6alpAwFAw4FAwwFAwwFAwwFAwkDAgkDAgoDAgoDAgkDAgcD - AgoDAgkDAgUCAQUCAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9lJKdlIJ9ZGJtZGJtNGJs9EJcpDJMRBI8lCJMA/Ir0/Ir0/Irs9Ibg9Ibs9Ib0/IrM7ILQ7ILY8 - IbE7ILM7IK05H6w5H6o4Hq05H6g3Hp40HKM2HaI2HaA1HZ40HKM2HZkyG5w0HJw0HJszHJszHJkyG5sz - HJIwGpQxG5kyG5cyG5kyG5szHJkyG48wGpYyG5IwGpIwGo0uGZEwGpEwGo0uGY0uGZIwGpEwGpYyG4gt - GIsuGYYsGJszHKo4Hqc3HsRBI9pLK8RBI6I2HY8wGogtGIUsGIUsGIgtGI0uGYsuGYouGYMsGIAqF3ko - FnkoFnooFncnFXUnFXImFXUnFXAlFHAlFHQnFW8lFGsjE28lFGkjE2sjE2kjE2gjE2MhEmQhEmMhEmMh - EmEhEl8fEVoeEFwfEVcdEFoeEFwfEVwfEVoeEFcdEFMcD1AbD1AbD04aDk0aDksZDksZDk0aDkkYDUsZ - DkcYDUkYDUYYDUIWDEQWDEEWDEIWDD8VDD0UCzwUCzoUCzoUCzoUCzgSCjgSCjUSCjMRCTMRCTMRCTEQ - CTEQCTAQCS4QCS4QCSsOCCkOCCsOCCsOCCkOCCkOCCkOCCUNByQMByILBiILBiILBiALBiALBiILBh8L - Bh0KBhsJBRsJBRsJBRsJBRoJBRoJBRgJBRgJBRgJBRYHBBQHBBMHBKalpP////////////////////// - /////////////////6alpAwFAw4FAw4FAwwFAwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgcDAgUCAQMB - AQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9VGJtZGJthH - JtVGJs9EJc9EJcxDJM5EJcRBI8JAI8JAI74/IsA/Ir0/IrY8IbM7ILg9IbY8Ibg9IbY8IbE7ILY8Ia86 - H6o4Hqw5H6g3HqM2HaU3HqA1Hac3HqA1HaA1HaM2HZszHJw0HJszHJszHJw0HJkyG5kyG5cyG5cyG5cy - G5IwGpIwGpcyG5kyG5IwGpYyG5QxG5QxG5cyG40uGZIwGo8wGo0uGYgtGJQxG5EwGo0uGZYyG6M2HbY8 - Ib0/ItpOLt1eQdpOLrQ7IJszHIouGYYsGIYsGIgtGIgtGJEwGpEwGoAqF34qF3koFnUnFXwpFnUnFXUn - FXcnFXUnFXQnFXQnFWsjE3AlFHImFWsjE2gjE2sjE2gjE2YhEmYhEmYhEmMhEl8fEV8fEV8fEVwfEVwf - EVoeEFoeEFwfEVgdEFgdEFUcD1McD1UcD1AbD04aDk0aDk0aDksZDk0aDksZDksZDkcYDUYYDUYYDUQW - DEIWDEIWDD8VDD8VDDwUCz0UCz0UCzwUCzgSCjgSCjYSCjUSCjMRCTMRCTMRCTMRCTAQCS4QCTAQCS4Q - CSsOCCkOCCkOCCkOCCkOCCcNBycNByUNByQMByQMByILBiQMByALBiALBiALBh8LBh0KBh0KBhsJBR0K - BhsJBRsJBRgJBRgJBRgJBRgJBRYHBBMHBBEGA6alpP////////////////////////////////////// - /6alpA4FAwwFAwwFAw4FAwwFAwoDAgoDAgoDAgoDAgoDAgkDAgcDAgcDAgcDAgMBAQMBAQIBAQIBAQMB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pNLdhHJthHJtZGJthHJspDJM5E - JdFFJcxDJMxDJMRBI8RBI8JAI70/Irk9IbE7ILY8IbQ7ILY8IbE7IK86H7Q7IK05H686H6c3Hqw5H6g3 - Hqc3HqU3HqI2Hac3HqI2Hag3HqI2Hac3HpszHJszHJ40HKA1HZw0HJYyG5YyG5YyG5cyG5YyG5szHJQx - G5cyG5cyG5EwGpYyG5YyG5EwGpYyG48wGpIwGpIwGpIwGo8wGpQxG5YyG6U3HsRBI9ZGJt1bPuBrUN1a - PMdCJKU3Ho8wGoouGYouGYgtGIYsGI8wGpEwGoYsGHwpFoAqF3koFnkoFnkoFnkoFnkoFncnFXQnFXQn - FXAlFHUnFXAlFGsjE2kjE2kjE2gjE2gjE2YhEmQhEmMhEmEhEmEhEl8fEV4fEVwfEV4fEV4fEVwfEVcd - EFgdEFcdEFMcD1AbD04aDlAbD04aDk0aDk4aDksZDkkYDUcYDUQWDEQWDEQWDEQWDEEWDEIWDD8VDD8V - DD0UCz0UCzwUCzoUCzYSCjgSCjUSCjYSCjMRCTMRCTMRCTEQCTEQCTAQCTAQCTAQCS4QCSwPCCsOCCkO - CCkOCCcNBykOCCcNByUNByUNByQMByUNByILBiALBiILBh8LBh0KBh0KBhsJBRsJBRsJBRsJBRgJBRoJ - BRoJBRgJBRQHBBMHBBEGA6elpP///////////////////////////////////////6alpA4FAwwFAw4F - AwwFAwoDAgoDAgoDAgoDAgoDAgkDAgcDAgcDAgkDAgkDAgMBAQIBAQIBAQIBAQMBAQMBAQIBAQIBAQIB - AQAAAAAAAAIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9lIJ9ZGJthHJtVGJthHJtFFJdFFJdFFJcxDJMdCJMRB - I70/Ir0/Irs9Ib0/Irs9Ibg9IbQ7ILM7ILE7ILE7ILg9Iaw5H7E7IKw5H6o4Hqg3Hqg3Hqc3Hqg3HqU3 - HqU3HqM2HaI2HZszHKA1HZszHJw0HJszHJszHJw0HJkyG5QxG5YyG5szHJw0HJkyG5QxG5QxG5cyG5Qx - G5cyG5IwGpYyG5cyG40uGZYyG5kyG5cyG5EwGpszHKw5H9NGJttRMt5gROFuVN5iRtVGJrQ7IJkyG5Iw - Go8wGoouGYouGZEwGpIwGogtGIUsGIErF4YsGHwpFnooFnUnFXkoFncnFXkoFncnFXQnFXcnFXImFW8l - FGsjE20kFGkjE2kjE2MhEmYhEmQhEmQhEmMhEmMhEl4fEV4fEV8fEVwfEVgdEFcdEFUcD1cdEFUcD1Ib - D1AbD1AbD04aDk0aDksZDkkYDUkYDUcYDUYYDUQWDEEWDEIWDEEWDEEWDEEWDD0UCz0UCz8VDDwUCzgS - CjUSCjYSCjYSCjgSCjYSCjUSCjMRCTEQCTMRCTAQCS4QCS4QCSwPCCwPCCsOCCsOCCsOCCcNBycNBycN - ByUNByUNByQMByQMByILBiALBh0KBh0KBh8LBhsJBRsJBRsJBRsJBRoJBRoJBRgJBRgJBRMHBBMHBBMH - BBEGA6alpP///////////////////////////////////////6alpAoDAgoDAgwFAwoDAgoDAgoDAgkD - AgkDAgcDAgkDAgkDAgkDAgcDAgkDAgUCAQIBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9pNLdlJKdlIJ9ZGJtNGJtNGJs5EJc5EJcpDJMxDJMxDJMVBI8dCJL4/IsA/ - IsA/Ir0/Irk9Ibs9Ibk9IbM7IK86H605H7E7IKo4Hq05H6A1Ha05H6I2HaU3HqU3Hqc3HqM2HaA1Hac3 - Hp40HJszHJ40HJ40HJw0HJw0HJszHJszHJszHJszHJszHJszHJ40HJszHJ40HJszHJcyG5szHJw0HJ40 - HJQxG5szHJYyG5szHJszHJ40HLM7INlIJ91bPuBoTeFwV99kSNpNLcA/Iqg3HpcyG5EwGoouGYsuGZcy - G5YyG5EwGosuGYMsGIMsGIUsGIAqF3koFnkoFncnFXUnFXcnFXcnFXQnFXQnFW8lFG8lFG8lFG0kFGkj - E2YhEmgjE2YhEmQhEmMhEmQhEl8fEV4fEVoeEFgdEFcdEFcdEFUcD1McD1UcD1IbD1IbD1AbD1AbD00a - Dk0aDksZDksZDkkYDUcYDUkYDUQWDEIWDEIWDD8VDEEWDD0UCz0UCzwUCzwUCzgSCjYSCjYSCjYSCjUS - CjUSCjUSCjMRCTEQCTEQCTAQCTAQCS4QCSwPCCsOCCkOCCkOCCkOCCcNByUNByUNByUNByQMByQMByUN - ByILBh8LBh8LBh8LBh8LBh0KBh0KBh0KBhsJBRsJBRoJBRgJBRQHBBQHBBMHBBMHBBEGA6alpP////// - /////////////////////////////////6alpAoDAgwFAwwFAwoDAgoDAgwFAwoDAgwFAwoDAgoDAgcD - AgcDAgkDAgcDAgMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9tSM9pLK9pOLtlJKdlIJ9NGJs9EJcxDJMpDJMdCJMdCJMpDJMVBI74/IsJAI70/Ir0/Irk9Ibk9 - Ibs9Ibg9Ia05H686H686H605H6w5H6o4Hq05H6U3Hqg3HqU3Hqc3HqI2HaM2Hag3HqA1HaI2HaM2HZky - G5kyG5w0HJszHKA1HZw0HJw0HJw0HKI2HaU3Hpw0HJszHJkyG5szHJszHKI2HZ40HJw0HJszHJw0HKM2 - HZw0HKU3Hrg9IdpOLt9kSOBrUOJyWeBoTdxXOcpDJK86H5w0HJYyG40uGY8wGpszHJszHJYyG48wGoYs - GIMsGIUsGIErF4AqF34qF3koFnkoFnkoFnooFnQnFXAlFHImFW8lFHAlFG0kFG0kFGsjE2gjE2YhEmMh - EmMhEmQhEmQhEl8fEV4fEVwfEVoeEFcdEFcdEFMcD1UcD1UcD1McD04aDlAbD04aDk0aDk0aDkkYDUsZ - DkkYDUYYDUQWDEQWDEYYDT8VDD8VDD0UCzwUCzoUCzoUCzoUCzgSCjYSCjUSCjUSCjMRCTMRCTMRCTAQ - CTEQCTAQCTAQCS4QCS4QCSsOCCsOCCkOCCUNBycNByUNBykOCCUNByUNByQMByUNByQMByILBiILBh8L - Bh8LBh0KBh8LBhsJBRsJBRsJBRsJBRYHBBMHBBMHBBMHBBMHBBMHBKalpP////////////////////// - /////////////////6alpAwFAwwFAw4FAwwFAwwFAwwFAwoDAgwFAwwFAwkDAgkDAgcDAgkDAgcDAgUC - AQMBAQIBAQIBAQMBAQMBAQIBAQAAAAIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lJKdlIJ9lJ - KdlJKdZGJtFFJc9EJdNGJspDJM5EJcxDJMlCJMdCJMVBI8dCJL0/Ir4/Ir0/Irg9Ibk9Ibs9Ia86H7Q7 - IKw5H6w5H6w5H6A1HaM2HaU3HqM2HaU3HqU3HqM2HaU3HqU3HqU3HqI2HZ40HJ40HJw0HJszHJw0HKA1 - HZ40HJ40HJ40HJw0HJw0HKU3HqA1HZ40HJw0HJszHKI2HaM2HZ40HJw0HKA1HaA1HaI2Hao4Hr4/IttU - NeBoTeBrUOJyWeBrUN1cP9NGJrs9Iac3Hpw0HJIwGpIwGpkyG5w0HJYyG4suGYouGYYsGIYsGIMsGIEr - F4ErF3wpFnkoFnkoFnwpFnkoFnQnFXUnFXAlFHAlFG0kFGsjE2QhEmYhEmYhEmQhEmgjE2MhEmQhEl8f - EWEhEl4fEVgdEFoeEFcdEFcdEFUcD1UcD1UcD04aDk4aDk0aDk0aDk0aDkkYDUsZDkYYDUYYDUIWDEIW - DEIWDD8VDEEWDD0UCzwUCz0UCzoUCzwUCzoUCzYSCjUSCjUSCjMRCTUSCjMRCTMRCTEQCTAQCTAQCS4Q - CS4QCSsOCCwPCCsOCCkOCCsOCCkOCCkOCCcNByUNByQMByUNByQMByILBiILBh8LBh8LBh8LBh0KBhsJ - BRsJBRsJBRgJBRQHBBMHBBMHBBMHBBMHBBMHBKalpP////////////////////////////////////// - /6alpAwFAw4FAw4FAwwFAwwFAwwFAwoDAgkDAgkDAgkDAgcDAgkDAgcDAgcDAgcDAgMBAQIBAQIBAQMB - AQMBAQIBAQAAAAIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9lJKdpLK9pLK9pNLdlJKdlJKdhH - JtZGJtNGJsxDJMxDJMpDJMdCJMlCJMlCJMdCJL4/IrY8Ibg9Ibk9Ibg9Ibk9IbY8Ia05H7E7ILM7IKc3 - HrE7IKg3Hq05H6c3HqM2HaI2HaU3Hqc3HqA1HaM2HZw0HKM2HaA1HZszHJw0HKM2HaI2HZw0HKM2HZsz - HKA1HaI2HaM2HZw0HKI2HZw0HJw0HKU3Hpw0HJw0HJ40HKA1Hao4HrE7IMlCJN1aPOBrUOFvVeN3XuFu - VN5gQ9lJKcRBI605H6A1HZszHJszHJkyG540HJcyG40uGYsuGY0uGYsuGYgtGIUsGIMsGIAqF34qF3oo - FnkoFnooFnQnFXAlFG8lFHQnFW0kFGsjE2gjE2gjE2gjE2QhEmYhEmQhEmEhElwfEVoeEF4fEVgdEFwf - EVgdEFUcD1UcD1McD1McD04aDlAbD04aDksZDkkYDUkYDUkYDUYYDUYYDUQWDEQWDEcYDUQWDD8VDDwU - Cz0UCzwUCzoUCzoUCzgSCjgSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTAQCTAQCS4QCS4QCSwPCCsOCCkO - CCsOCCkOCCcNByUNBycNByUNByILBiALBiALBiALBiALBiALBh0KBh0KBhsJBR0KBhoJBRgJBRYHBBQH - BBQHBBQHBBMHBBMHBBEGA6alpP///////////////////////////////////////6alpAwFAwwFAwwF - AwwFAwwFAwwFAwoDAgkDAgkDAgkDAgkDAgcDAgkDAgcDAgcDAgMBAQIBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9pOLtlJKdlJKdlJKdlIJ9lIJ9VGJthHJsxDJM9EJclC - JMVBI8lCJMRBI8RBI8VBI8VBI70/Irk9IbQ7ILk9IbY8Ibk9IbM7ILg9IbM7IKw5H6o4Hq05H7E7IKg3 - Hqo4HqU3Hqg3Hp40HKI2HaA1HZ40HKA1HZ40HKA1HaI2HaA1HaA1HaA1HaA1HaM2HaA1HaA1Hao4Hqc3 - Hp40HKI2HaM2HZ40HKU3Hqc3Hqc3Hqw5H7Y8IcJAI9NGJt1eQeFwV+FwV+N4YOFwV99mS9pPMMxDJLg9 - Iac3HqM2HZszHJcyG5cyG5QxG5EwGo0uGY0uGY8wGoouGYgtGIYsGIMsGIMsGH4qF3wpFnooFnUnFWsj - E3cnFXQnFW0kFGsjE2kjE2gjE2YhEmgjE2YhEl8fEVwfEV8fEVoeEFoeEFoeEFwfEVoeEFcdEFMcD1Mc - D1cdEE4aDksZDksZDksZDkcYDUkYDUkYDUcYDUcYDUYYDUIWDEIWDEIWDD8VDD0UCzwUCz0UCzwUCzwU - CzgSCjYSCjUSCjYSCjMRCTMRCTMRCTEQCTMRCTEQCTEQCS4QCS4QCS4QCSsOCCcNBycNBykOCCcNBycN - BycNByUNByQMByILBiALBiALBh8LBiALBh0KBh0KBh0KBhsJBRgJBRYHBBQHBBQHBBYHBBQHBBMHBBQH - BBMHBKalpP///////////////////////////////////////6alpAwFAwwFAwoDAgwFAwoDAgoDAgkD - AgkDAgoDAgkDAgkDAgkDAgcDAgcDAgcDAgMBAQMBAQIBAQIBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQAA - AAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9pOLtpNLdpLK9lIJ9hHJtlIJ9VGJtVGJtZGJsxDJM9EJcpDJMpDJMJAI8VB - I8RBI8JAI70/IsRBI70/Ir0/Irg9IbQ7ILQ7ILM7ILQ7IKw5H6w5H605H686H6o4Hqc3Hqo4HqI2Hao4 - Hqg3HqI2HaI2Hac3Hqc3HqM2HaM2HaA1HaU3Hqc3HqI2HaM2HaI2Hag3Hqo4Hqc3HqM2HaU3HqM2Hao4 - HqU3Hqc3Hq05H7M7IMA/IsdCJNlJKd5gRON4YON7Y+R8ZeN3XuBrUN1aPNlJKcA/IrQ7IKg3HqA1HZsz - HJszHJszHJYyG5QxG48wGosuGY0uGY0uGYsuGYsuGYouGYErF4MsGH4qF3koFm8lFHAlFHAlFHImFWkj - E2gjE2kjE2kjE2kjE2QhEmQhEmEhEl4fEVwfEVgdEFoeEF4fEVoeEFgdEFUcD1McD1IbD1IbD04aDksZ - Dk0aDksZDksZDkcYDUcYDUQWDEYYDUIWDD8VDD8VDD0UCz8VDDwUCzwUCzoUCzoUCzoUCzoUCzYSCjgS - CjUSCjMRCTMRCTMRCTEQCTEQCTEQCTAQCSwPCC4QCSwPCCsOCCkOCCsOCCcNByUNBycNByUNByILBiQM - ByILBiALBiALBh8LBh0KBh0KBh0KBhoJBRYHBBYHBBQHBBQHBBQHBBQHBBEGAxEGAxEGA6alpP////// - /////////////////////////////////6alpA4FAwwFAwoDAgwFAwoDAgwFAwoDAgoDAgwFAwoDAgkD - AgcDAgkDAgcDAgcDAgUCAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9pPMNpPMNpNLdhHJtpNLdZGJthHJthHJtFFJdNGJs9EJdFFJclCJMVBI8lCJMdCJL0/Ir4/Irs9 - IcA/Ir0/Irs9Ibs9IbY8Ibs9Ia05H605H7E7IKw5H7E7IKw5H6w5H605H6g3Hqo4Hqw5H6U3Hqo4HqU3 - HqM2HaM2Hag3HqU3HqM2HaU3HqM2HaM2Hac3Hqg3Hqc3Hqo4Hqo4Hqo4Hqw5H6w5H7E7IK86H7Q7ILs9 - IcpDJNZGJttRMuBrUOR9ZuR9ZuR/aON7Y+JyWd5gRNpOLslCJLs9Ia86H6o4HqU3Hqc3Hpw0HJw0HJky - G5EwGpIwGpEwGo8wGosuGYouGYErF4AqF4ErF34qF3wpFnQnFXImFXImFW8lFHAlFG0kFG8lFG0kFGkj - E2QhEmEhEmMhEl8fEV4fEVoeEFcdEFoeEFcdEFcdEFcdEFIbD1AbD04aDk4aDksZDk4aDkkYDUkYDUkY - DUkYDUYYDUQWDEEWDEEWDEEWDD8VDD8VDDwUCzwUCzoUCzgSCjoUCzgSCjUSCjMRCTMRCTUSCjMRCTMR - CTEQCTEQCTEQCTAQCS4QCSwPCCwPCCwPCCkOCCkOCCkOCCcNBycNByUNByUNByQMByILBiILBiALBh8L - Bh8LBhsJBRoJBRgJBRYHBBYHBBYHBBQHBBQHBBMHBBQHBBEGAxEGA6alpP////////////////////// - /////////////////6alpAwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgkDAgoDAgkDAgcDAgcDAgUC - AQUCAQMBAQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9pOLtpNLdpO - LtlIJ9lJKdhHJtpNLdVGJtVGJtFFJdZGJtVGJs5EJcxDJMlCJMdCJMJAI70/IsA/Ir0/IsJAI8A/Ir4/ - Irs9Ibs9Ia86H7Q7ILg9IbE7IK86H605H7E7IK86H605H686H6w5H6w5H6g3Hqw5H6I2Hao4Hq05H6U3 - Hqo4Hqg3Hqo4Hq86H605H6o4Hqw5H686H7Q7IK05H7Q7ILM7ILY8Ib0/Ir0/IsA/Is5EJdpLK9xYOuBq - T+R9ZuWBauWCbOR9ZuJ1XOBqT9xVN9VGJsRBI7s9IbM7IKo4Hqo4Hqc3Hqc3HqI2HZ40HJszHJYyG5Yy - G5QxG4suGYgtGIUsGIYsGIErF34qF3koFnUnFXQnFXAlFHAlFG8lFG8lFGgjE2gjE2YhEl8fEWEhEmEh - ElwfEV8fEVoeEF4fEVgdEFUcD1cdEFUcD1McD1McD00aDk4aDlAbD00aDksZDksZDkcYDUcYDUYYDUIW - DEIWDEEWDD0UCz0UC0EWDD0UCz0UCzwUCzoUCzgSCjUSCjMRCTMRCTMRCTMRCTMRCTEQCTEQCTAQCTAQ - CS4QCSwPCCsOCC4QCSsOCCkOCCcNBycNBycNByILBiQMByQMByILBiILBh8LBh8LBh0KBhoJBRgJBRYH - BBYHBBYHBBYHBBQHBBQHBBMHBBMHBBEGAxEGA6alpP////////////////////////////////////// - /6alpA4FAwwFAwkDAgoDAgwFAwwFAwoDAgoDAgoDAgkDAgkDAgkDAgcDAgcDAgUCAQUCAQMBAQIBAQMB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pOLttRMtpNLdpOLtpLK9pLK9lJ - KdpLK9ZGJtNGJtNGJtFFJcxDJM5EJcRBI85EJcVBI8RBI8RBI8RBI70/Ir0/Irs9Ibk9Ib4/Irg9IbQ7 - ILg9IbE7IKw5H7M7ILM7ILY8Ia05H605H605H6g3HrE7IKo4Hqo4Hqw5H605H6g3Hqw5H6o4Hq86H686 - H605H6w5H686H7g9Ib0/Irs9Ib0/Irg9Ibk9IcJAI74/IsRBI9VGJttUNd5gQ+FuVOaFb+aHcuaHcuWB - auR8ZeFuVN1cP9pNLdFFJcA/Irk9IbM7IKw5H605H6w5H6w5H6U3Hp40HJszHJszHJcyG48wGosuGYgt - GIUsGIMsGIUsGHooFnwpFnkoFncnFXImFXQnFXAlFGkjE2sjE2YhEmYhEmYhEmMhEl8fEV4fEV4fEV8f - EVwfEVcdEFoeEFcdEFUcD1UcD1McD04aDk0aDksZDksZDkkYDUcYDUYYDUcYDUIWDEYYDUQWDD8VDD0U - Cz0UCz0UCzwUCzwUCzgSCjgSCjYSCjYSCjYSCjUSCjMRCTMRCTEQCTEQCTAQCS4QCSwPCCsOCCsOCCwP - CCkOCCcNBycNBycNByQMByILBiQMByQMByILBiALBh8LBiALBh0KBhgJBRoJBRgJBRYHBBYHBBYHBBQH - BBYHBBQHBBMHBBMHBBMHBKelpP///////////////////////////////////////6alpA4FAwwFAwoD - AgoDAgoDAgoDAgwFAwkDAgoDAgkDAgoDAgcDAgcDAgcDAgcDAgcDAgMBAQIBAQIBAQMBAQMBAQMBAQIB - AQMBAQIBAQAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9pPMNtSM9pLK9pLK9pOLtpLK9VGJtNGJtZGJtZGJtVG - JtNGJtNGJspDJMpDJMlCJMlCJMdCJMlCJMRBI70/Ir4/IsA/Ir4/IsA/Irk9IbY8IbM7ILE7ILM7IK86 - H7Q7ILM7ILQ7IK05H7M7ILQ7IK05H6o4HrE7ILM7IK86H7E7IK86H7Q7ILM7ILQ7ILM7ILk9Ibs9Ibs9 - Ib4/IsJAI70/Ir0/Ir0/IsVBI8lCJM9EJdpNLd1aPN9kSOJ0W+aHcuiPe+eLduaHceR/aOJ0W99mS9tU - NdhHJsdCJMJAI70/Irk9IbQ7IKw5H6w5H6g3Hqg3HqA1HaI2HZkyG5szHJcyG5EwGosuGYYsGIUsGIAq - F34qF3ooFnkoFnImFXImFXAlFG0kFGsjE2gjE2YhEmMhEmQhEmMhEl8fEVwfEVgdEFwfEVoeEFcdEFcd - EFcdEFcdEFIbD00aDk4aDkkYDUsZDk0aDksZDkcYDUQWDEIWDEEWDEIWDD0UCzwUCzwUCzwUCzwUCzoU - CzgSCjYSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTEQCTAQCTAQCSwPCCsOCCwPCCsOCCsOCCcNByUNByUN - ByQMByQMByQMByQMByQMByALBh0KBh0KBh0KBhgJBRoJBRgJBRgJBRYHBBYHBBQHBBQHBBQHBBQHBBMH - BBEGA6alpP///////////////////////////////////////6alpA4FAw4FAwoDAgoDAgwFAwoDAgoD - AgoDAgwFAwkDAgkDAgoDAgcDAgkDAgkDAgcDAgMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9pOLtpPMNpOLttSM9pNLdlJKdlJKdZGJtZGJtZGJtVGJs9EJdNGJs5EJcpD - JMVBI8lCJMVBI8lCJMA/IsJAI74/Ir0/Ir0/Irg9Ib0/IsA/Irk9Ibk9IbM7ILQ7ILg9IbY8Ia86H7Y8 - Ibs9IbQ7ILg9IbQ7ILM7ILg9Ibk9IbE7ILM7ILk9Ibg9Ibg9Ibk9Ib0/Ir4/IsJAI8RBI74/IsA/IsVB - I8dCJMlCJNFFJdhHJtpPMN5gROFuVON5YeeLdumTgOeNeeaHcuWCbON7Y+BrUN1cP9pOLthHJsxDJMA/ - Irs9Ibk9IbQ7ILM7IKo4Hqg3Hqg3Hqc3HqA1HZszHJszHJcyG4suGYsuGYYsGIUsGIErF3wpFnkoFnko - Fm8lFG0kFG0kFGkjE2kjE2YhEmMhEmEhEmEhEl4fEVoeEFoeEFoeEFoeEFcdEFcdEFMcD1IbD1McD1Ab - D04aDksZDksZDkkYDUcYDUcYDUQWDEQWDEEWDD8VDEEWDD0UCzwUCz0UCzoUCzoUCzoUCzgSCjYSCjUS - CjMRCTMRCTMRCTEQCTEQCTEQCTAQCTAQCSwPCCwPCCwPCCsOCCsOCCcNBykOCCcNByUNByUNByQMByIL - BiILBh8LBhsJBRoJBRsJBRoJBRoJBRgJBRYHBBYHBBgJBRMHBBMHBBMHBBMHBBMHBBMHBKalpP////// - /////////////////////////////////6alpAwFAwwFAwoDAgoDAgoDAgwFAwkDAgkDAgoDAgkDAgkD - AgkDAgcDAgcDAgcDAgcDAgMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9pPMNpOLttSM9pPMNpOLtpOLtlJKdZGJtZGJthHJtlIJ9VGJs9EJdFFJc5EJdFFJcxDJMpDJMVB - I74/IsA/IsJAI70/Ir4/Ir0/IsA/Irs9Ibk9Ibs9Ibk9Ibs9Ibg9Ibg9Ibs9Ibk9IbQ7ILY8IbQ7ILg9 - Ibk9IbQ7ILs9Ib0/Irk9Ibs9Ib0/Irs9IcA/IsRBI8RBI8JAI8lCJMxDJM5EJclCJMpDJNNGJtlIJ9pP - MNxYOuBrUOJ1XOWBauiPe+mWg+iSfueMd+aHcuR9ZuFvVd5iRttUNdlJKdFFJclCJL4/Ir0/Ir4/Irk9 - Ia86H7M7IK05H6o4Hqc3HqU3Hp40HJszHJQxG5EwGo8wGoYsGIMsGIMsGHooFnUnFXImFXImFW8lFGsj - E2sjE2gjE2QhEmMhEmEhEmMhElwfEVoeEFgdEFgdEFcdEFUcD1McD1IbD1AbD1McD00aDk0aDksZDksZ - DkcYDUYYDUIWDEQWDEEWDEEWDD8VDD0UCz0UCzwUCzwUCzgSCjgSCjgSCjYSCjUSCjUSCjMRCTMRCTMR - CTMRCTAQCTAQCTAQCS4QCS4QCS4QCSsOCCsOCCkOCCsOCCcNByUNBycNByUNByILBh0KBhsJBR0KBhoJ - BR0KBhsJBRgJBRgJBRYHBBYHBBQHBBMHBBMHBBMHBBQHBBMHBBMHBKalpP////////////////////// - /////////////////6WkpAoDAgoDAgwFAw4FAwoDAgoDAgoDAgkDAgkDAgcDAgkDAgkDAgcDAgkDAgkD - AgcDAgUCAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9pOLtpOLttR - MttSM9pOLtpOLtpNLdZGJtlIJ9lIJ9hHJtNGJtFFJdNGJtNGJsdCJM9EJcxDJMVBI74/Ir4/IsVBI8RB - I70/IsA/Ir0/Ir4/Ir0/IsA/Ir0/Ir4/Irk9Ib0/Irg9Ibk9Ib0/IrQ7ILg9Ibk9Ibg9IbY8Ibk9Ibs9 - Ib0/IsRBI8A/IsRBI8lCJMdCJMlCJM9EJc5EJdFFJdVGJtVGJtFFJdpLK9tSM91aPN5gQ+FuVON5YeWD - beiPe+qciumWg+iPe+eLduR/aOJ1XOBoTd1aPNtRMtZGJs9EJclCJMlCJMJAI7k9Ibk9Ibg9IbM7IKo4 - Hqw5H6c3Hqg3HqM2HZszHJcyG5YyG48wGogtGIErF3wpFnkoFnQnFXUnFXImFW0kFG0kFGYhEmQhEmEh - El8fEWEhEl4fEVgdEFcdEFoeEFcdEFcdEFgdEFMcD1AbD1AbD1IbD00aDkkYDUcYDUkYDUQWDEIWDEEW - DEEWDEIWDD8VDD8VDD0UCz0UCzoUCzgSCjgSCjYSCjUSCjUSCjUSCjMRCTMRCTMRCTMRCTAQCTAQCS4Q - CTAQCS4QCSwPCCsOCCsOCCsOCCsOCCQMByQMByUNByILBiILBh8LBh0KBhoJBRgJBRsJBRoJBRgJBRYH - BBYHBBYHBBMHBBQHBBQHBBMHBBQHBBEGAxMHBG1qaaelpKalpKalpKalpKalpKalpKalpKalpKalpKal - pGxpaA4FAwwFAwwFAwwFAwwFAwwFAwoDAgkDAgkDAgkDAgoDAgkDAgkDAgcDAgkDAgcDAgUCAQMBAQMB - AQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9tRMtpOLtpOLtpLK9pOLtpLK9hH - JtpNLdVGJtZGJtNGJtZGJtlJKdNGJtNGJs5EJc9EJclCJMpDJMVBI8VBI8VBI8JAI8dCJMA/IsJAI8A/ - Ir0/Ir0/Ir4/IsA/IsA/IsVBI7s9Ib0/Ir0/IsRBI70/Ir0/IsJAI70/Ir0/IsA/IsA/IsVBI8lCJMxD - JM5EJc5EJc9EJc9EJdZGJtNGJthHJtpLK9pNLdpPMN1aPN5iRuBrUON3XuR/aOaJdOiRfeufjuqZh+mT - gOeMd+aHceN7Y+BsUt5iRttUNdpNLdVGJsxDJMVBI8dCJMVBI70/Ir0/Irs9IbQ7ILE7IKg3HqM2HaA1 - HZw0HJszHJYyG48wGogtGIMsGIAqF34qF3koFnUnFXQnFWsjE2kjE2kjE2kjE2kjE2EhEl8fEV4fEVoe - EFcdEFoeEFcdEFUcD1UcD1AbD1IbD04aDk4aDlAbD0kYDUkYDUYYDUQWDEYYDUIWDEIWDEQWDD8VDD8V - DD8VDD0UCzwUCzoUCzgSCjgSCjYSCjYSCjMRCTMRCTMRCTMRCTEQCTAQCTEQCS4QCTAQCS4QCSwPCCwP - CCsOCCkOCCkOCCUNByQMByQMByALBh8LBh8LBhsJBRsJBRoJBRoJBRgJBRgJBRYHBBYHBBYHBBQHBBQH - BBMHBBMHBBMHBBEGAxMHBBMHBBEGAxEGAxEGAxEGAw8FAw8FAw8FAw4FAw8FAw4FAwwFAw4FAwwFAwoD - AgwFAwoDAgoDAgoDAgoDAgkDAgkDAgoDAgkDAgkDAgcDAgkDAgcDAgUCAQMBAQIBAQIBAQMBAQIBAQMB - AQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////91bPt1bPttSM9pNLdpOLtpNLdpPMNpOLtlIJ9lJKdlI - J9FFJdZGJs5EJdFFJdNGJs9EJcpDJMpDJMdCJMlCJMdCJMRBI8RBI8RBI8dCJMA/IsdCJMA/Ir4/IsRB - I7s9IcdCJL4/Ir0/Ir4/IsRBI8A/IsdCJMRBI8lCJMdCJMRBI8pDJMpDJM5EJc9EJdlIJ89EJdVGJtZG - JtlJKdlIJ9lJKdtSM9xVN9xXOd1aPOBoTeFuVON7Y+aFb+eNeemXheyikeufjumXhemTgOaJdOWBauJ0 - W99mS91aPNtRMtpNLdZGJthHJtFFJcRBI8RBI8RBI7s9Ib0/IrY8IbE7IKw5H6I2HZ40HJw0HJw0HJsz - HI8wGoYsGIErF34qF3koFnooFnQnFW0kFGsjE2YhEmYhEmMhEmMhEl8fEVoeEFwfEVcdEFgdEFcdEFcd - EFIbD1UcD1McD04aDk0aDk0aDkkYDUcYDUYYDUQWDEQWDEYYDUIWDEEWDEEWDEEWDD8VDD0UCz0UCzoU - CzYSCjYSCjUSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTAQCS4QCTAQCSwPCCwPCCsOCCsOCCkOCCkOCCcN - ByQMByILBiALBh8LBh8LBh0KBh0KBhsJBRoJBRgJBRgJBRoJBRYHBBQHBBQHBBQHBBMHBBMHBBEGAxEG - AxMHBBMHBBMHBBEGAxEGAw8FAw8FAw8FAw8FAw4FAw4FAw8FAwwFAwwFAwwFAwwFAwoDAgwFAwwFAwoD - AgoDAgwFAwkDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9xXOdtUNdpPMNtUNdxVN9tRMtpLK9pNLdVGJtlIJ9lJKdlJKdZGJtNGJtNG - JspDJMxDJMpDJMpDJMlCJM9EJcdCJMdCJMdCJMRBI8JAI8RBI8JAI74/IsJAI74/IsVBI8VBI8RBI8lC - JMRBI8VBI8RBI85EJcdCJMlCJMpDJM5EJc5EJdZGJtFFJdFFJdhHJthHJthHJtlIJ9pNLdpOLttSM9xV - N91eQd5gRN9lSuFwV+N3XuWDbeeMd+mVguufjuyklOyklOufjuqaiOmTgOaHcuR8ZeJyWd9lSt1eQdtR - MtpNLdVGJtFFJcpDJMlCJMVBI8JAI8RBI7s9IbE7IK05H6g3Hqg3HqA1HaA1HZszHJQxG40uGYYsGIEr - F3wpFnooFnUnFW8lFG0kFGsjE2gjE2QhEmkjE18fEV4fEVwfEVwfEVwfEVgdEFcdEFMcD1McD1UcD1Ib - D1IbD00aDksZDksZDkYYDUYYDUQWDEIWDEIWDEQWDEIWDEEWDD0UCzwUCzwUCzgSCjYSCjYSCjYSCjYS - CjYSCjMRCTMRCTMRCTEQCTEQCTAQCS4QCSwPCCsOCCwPCCsOCCkOCCkOCCcNByQMByILBiILBiALBh0K - Bh0KBhsJBRsJBR0KBhoJBRgJBRgJBRoJBRgJBRYHBBgJBRYHBBMHBBQHBBEGAxEGAxEGAxMHBBEGAxEG - AxEGAxEGAw8FAxEGAw8FAw4FAwwFAwwFAwwFAwwFAw4FAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgkDAgkD - AgkDAgkDAgkDAgcDAgkDAgcDAgMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9tUNdtRMttRMtxVN9pOLttRMttUNdpPMNpNLdpOLtlJKdhHJtZGJtVGJtVGJtFFJdVGJtNGJspD - JMpDJNFFJclCJMVBI8lCJMdCJMdCJMlCJMVBI8VBI8VBI8RBI8VBI8JAI8dCJMpDJMJAI8VBI8VBI8dC - JM9EJclCJM5EJcpDJNhHJtZGJtVGJthHJtpOLtpLK9pNLdtRMttRMtxXOd1aPN1cP99kSOBoTeBsUuN3 - XuR/aOaHcuiSfuqaiOugj+2qm+2omeyjk+ugj+mWg+iPe+WCbON3XuBrUN9kSN1bPttUNdpOLtlJKdVG - JtFFJc5EJcdCJMA/Irk9IbM7ILM7ILE7IKw5H6M2HZ40HJw0HJkyG5EwGo0uGYMsGIAqF3koFnQnFXIm - FXAlFG0kFG0kFGgjE2YhEl8fEWMhEl4fEV4fEVgdEFcdEFcdEFUcD1McD1McD1IbD1McD00aDksZDksZ - DkcYDUQWDEQWDEIWDEQWDEQWDEEWDD8VDDwUCzoUCzoUCzoUCzgSCjgSCjUSCjMRCTMRCTMRCTMRCTMR - CTEQCTEQCTAQCS4QCS4QCSsOCCwPCCkOCCcNBycNByILBiQMByALBiALBiALBiALBh8LBhsJBRsJBRoJ - BRoJBRoJBRoJBRgJBRYHBBQHBBQHBBYHBBQHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAw8FAw8FAw8FAw8F - Aw8FAw8FAw4FAw4FAwwFAwwFAwoDAgwFAwoDAgoDAgoDAgoDAgoDAgkDAgkDAgoDAgkDAgkDAgkDAgcD - AgcDAgcDAgMBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9tRMttRMtpO - LtpPMNpOLtpNLdpLK9pNLdtSM9tSM9lJKdlJKdlIJ9hHJthHJtVGJtVGJs5EJdFFJdFFJcxDJM9EJcxD - JMlCJMdCJMlCJMdCJMlCJMlCJMlCJMpDJMVBI8xDJMpDJMxDJM5EJcxDJMlCJM5EJcxDJMxDJNFFJdZG - JtlIJ9lIJ9pOLtpNLdtRMtxVN9xXOd1aPNtUNdxXOd5gQ95iRt9mS+FvVeJ0W+R9ZuaHcuiRfemXheuf - juyklO6un+6sneymlu2omOqciumVguaJdOR9ZuFwV+BrUN9lStxYOtxVN9tRMtpNLdZGJs9EJc5EJcdC - JMJAI70/Ir0/IrM7IKw5H6g3HqA1HaU3Hpw0HJkyG5YyG4suGX4qF3ooFnkoFnkoFnQnFWgjE2YhEmYh - EmQhEmMhEmMhEl4fEV4fEVoeEFoeEFcdEFUcD1IbD1IbD1IbD1AbD00aDksZDkcYDUYYDUQWDEYYDUQW - DEEWDEIWDEEWDEIWDD0UCzwUCzgSCjgSCjgSCjoUCzYSCjYSCjMRCTMRCTMRCTMRCTEQCTAQCTEQCTAQ - CS4QCSsOCCkOCCcNByUNByQMByILBiILBh8LBh8LBiALBiALBh0KBhsJBRsJBRoJBRsJBRoJBRgJBRoJ - BRgJBRQHBBYHBBQHBBQHBBEGAxEGAxMHBBEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAxEGAw8FAw4FAw4F - AwwFAw4FAwoDAgoDAgwFAwwFAwwFAwoDAgkDAgkDAgkDAgoDAgkDAgcDAgcDAgcDAgcDAgcDAgMBAQMB - AQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pOLtxVN9tSM9pOLtpPMNpOLtpN - LdNGJtZGJtlJKdpOLttRMtpOLtpLK9hHJtZGJtZGJtZGJtFFJdVGJsxDJMxDJNNGJs5EJc5EJcxDJMxD - JMlCJMVBI8lCJM5EJclCJM5EJc9EJc5EJcpDJM9EJdVGJtVGJtVGJs9EJdlIJ9pNLdlJKdpLK9tRMttR - MttUNdxVN9xVN91aPN1cP99lSt9kSOBsUuFuVON4YOR9ZuaHcueNeeqZh+ufjuymlu2qm++wou+0p++w - ou6sneyklOqciuiPe+aHcuR/aOJ0W+BrUN5gQ91aPNxXOdpPMNpNLdlIJ9FFJcpDJMVBI8A/Ir4/Ir0/ - Irs9Ia86H6M2HaA1HaA1HZkyG5IwGogtGIYsGIAqF3ooFnkoFnQnFWsjE2kjE2YhEmYhEmQhEmQhEl4f - EVoeEFoeEFgdEFcdEFUcD1IbD1IbD1IbD1IbD1AbD00aDksZDkkYDUcYDUcYDUYYDUEWDD8VDD8VDD0U - Cz0UCz0UCzwUCzoUCzoUCzgSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTAQCTEQCTAQCS4QCSsOCCkOCCcN - ByQMByILBiILBiILBiALBh8LBh8LBh0KBhsJBRoJBR0KBhsJBRsJBRsJBRoJBRoJBRYHBBQHBBYHBBMH - BBMHBBMHBBEGAxMHBBEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAxEGAxEGAw4FAw4FAwwFAwwFAwwFAwoD - AgwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgcDAgcDAgcDAgkDAgkDAgcDAgMBAQMBAQIBAQIBAQIBAQIB - AQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9tSM9tRMttUNdtRMtpNLdpNLdpNLdlIJ9lIJ9hHJtlI - J9VGJthHJtpNLdpOLtpOLtlIJ9hHJtlJKdhHJtNGJs9EJcxDJM5EJdNGJs9EJcpDJM9EJcxDJNFFJcpD - JMpDJM9EJc9EJclCJNlJKc5EJdFFJdVGJtVGJtlJKdtRMtlJKdlJKdpOLttUNdxXOd1bPt1aPN1eQd1c - P95gRN9lSuBrUOFwV+N7Y+R8ZeaFb+eMd+mTgOqaiOyklO6sne+wou+zpfC5rO+zpe+ypO2qm+yjk+qZ - h+iRfeaHcuR8ZeJ1XOBrUN9lSt5gQ9xXOdtRMtpNLdhHJs9EJclCJMRBI8VBI9FFJdlIJ8lCJLM7IKc3 - Hpw0HJkyG5YyG48wGo0uGYMsGH4qF3koFnUnFXAlFGsjE2kjE2YhEmYhEmEhEl4fEVoeEFgdEFgdEFcd - EFgdEFcdEFIbD04aDk4aDk0aDksZDk4aDkcYDUkYDUkYDUQWDEIWDEIWDD8VDDwUCzwUCz0UCzoUCzwU - CzwUCzwUCzYSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTEQCS4QCSsOCCUNBycNByUNByILBiQMByUNByQM - ByILBiILBh8LBh0KBh0KBh8LBhsJBRoJBRoJBRoJBRoJBRoJBRYHBBQHBBQHBBQHBBQHBBMHBBQHBBEG - AxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwoDAgkDAgkDAgkD - AgoDAgkDAgkDAgkDAgkDAgkDAgcDAgkDAgkDAgcDAgMBAQIBAQMBAQIBAQMBAQMBAQMBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9tUNdtSM9tUNdtUNdpOLtpOLtpLK9pNLdlIJ9lJKdpNLdlIJ9ZGJtNGJthH - JtpNLdpLK9pNLdpPMNpLK9VGJs9EJc9EJc9EJcxDJNFFJcpDJNNGJtNGJs5EJcxDJM9EJdFFJc9EJdNG - JtVGJtZGJtNGJtVGJthHJtpOLttSM9pOLttRMtxVN9xXOd1aPN5iRt9kSN9kSN9lSuBsUuBqT+J1XON5 - YeWBauaHceaJdOmXheqciuyjk+2qm++wovC3qvG6rvK/s/C5rPC3qu+wou2omeyklOqZh+eMd+aHceR9 - ZuJ1XOBrUN9mS95gQ9xYOtpPMNpNLdlIJ9FFJclCJMlCJNhHJtpPMNVGJrg9Iao4HqU3Hpw0HJkyG5Yy - G5IwGoUsGH4qF3ooFnkoFncnFXImFW8lFGkjE2QhEmMhEl8fEV4fEVoeEFgdEFgdEFcdEFUcD1McD1cd - EFIbD1AbD0sZDk0aDkcYDUcYDUcYDUQWDEEWDEEWDEIWDD8VDD0UCz0UCzwUCzoUCzoUCzgSCjYSCjYS - CjUSCjMRCTMRCTMRCTEQCTEQCTAQCSsOCCkOCCcNBycNByQMByQMByUNByUNByQMByQMByILBiALBh8L - Bh8LBh8LBh0KBhsJBRoJBRgJBRYHBBgJBRYHBBQHBBQHBBMHBBYHBBMHBBMHBBEGAxMHBBMHBBEGAw8F - Aw8FAxEGAw4FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAw4FAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgkDAgkD - AgkDAgkDAgkDAgkDAgcDAgcDAgMBAQMBAQIBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9tUNdxYOttSM9pOLtpPMNpOLtpOLtpPMNpOLtlIJ9pLK9lIJ9VGJtFFJdFFJdZGJtZGJtVGJtZG - JthHJtZGJtZGJtVGJtNGJtNGJsxDJM9EJdFFJdNGJtNGJs5EJdNGJs9EJdhHJtZGJtlIJ9ZGJtZGJtpL - K9pNLdpPMNpOLtxVN91cP91cP95gQ+BoTeBoTd9lSuBqT+FuVOJyWeJ1XON7Y+R8ZeaHcueNeemTgOue - jO2omO6sne+ypPC2qfG8sPK/s/LDuPK/s/K/s/C3qu6uoO6sneyjk+mWg+eMd+aHcuR8ZeN3XuBrUN9l - St5gQ9tUNdxVN9pOLtFFJcxDJMpDJNFFJdVGJsxDJLQ7IKo4Hqo4HqU3HpszHJkyG5QxG4YsGIAqF3oo - FnUnFXUnFXQnFW0kFGkjE2YhEmMhEl4fEV4fEVwfEVgdEFoeEFgdEFcdEFMcD1UcD1McD1AbD00aDk0a - DkkYDUQWDEYYDUYYDUIWDEEWDEEWDD0UCzwUCzwUCzwUCzoUCzoUCzgSCjYSCjYSCjYSCjMRCTMRCTMR - CTEQCS4QCSwPCCkOCCkOCCcNBycNByUNBycNByQMByILBiALBh8LBh8LBh8LBh8LBh0KBh0KBhsJBRoJ - BRgJBRgJBRoJBRgJBRQHBBQHBBYHBBMHBBMHBBQHBBEGAxEGAxEGAxEGAxEGAw8FAxEGAxEGAw8FAw8F - Aw8FAw4FAw8FAw4FAwwFAwwFAwwFAw4FAwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkDAgkDAgcDAgcD - AgkDAgcDAgMBAQMBAQIBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9xXOdtRMttU - NdtSM9pPMNpLK9pOLtpNLdpLK9pPMNlJKdlIJ9ZGJtFFJdZGJthHJtNGJtVGJtlIJ9lIJ9ZGJthHJtlJ - KdpLK9lIJ9pLK9ZGJtNGJtZGJtZGJtVGJthHJtlJKdVGJtVGJtlIJ9lJKdpOLtpOLttRMtxXOdxXOdxX - Od1cP95gROBoTeBqT+BsUuBqT+FuVOJ1XOR8ZeWCbOWBauaHcemVgumWg+yikeyklO6un++zpfC5rPK/ - s/LDuPPFuvTKwfPFuvPFuvK/s/C5rO+wou2omeyikemXheiRfeaHceR/aON7Y+FuVN9lSt1cP9xYOtpP - MNlJKdVGJs5EJcVBI8RBI70/IrQ7IKc3Hqc3Hqc3Hp40HJszHJIwGo0uGYErF3wpFncnFXUnFXUnFW0k - FGgjE2sjE2MhEl4fEVoeEFoeEFgdEFgdEFcdEFcdEFUcD1McD1IbD1IbD00aDk0aDkcYDUcYDUcYDUYY - DUIWDEEWDD8VDD0UCzoUCzwUCzoUCzoUCzoUCzgSCjYSCjYSCjMRCTMRCTMRCTEQCTAQCSwPCCkOCCkO - CCsOCCkOCCcNBycNBycNByILBiILBiALBh0KBh8LBh8LBiALBhsJBRsJBR0KBhsJBRgJBRgJBRgJBRgJ - BRQHBBQHBBQHBBMHBBMHBBMHBBQHBBQHBBEGAxEGAw8FAw8FAxEGAxEGAw4FAw4FAw8FAw4FAw4FAw4F - AwwFAw4FAwwFAwoDAgwFAwoDAgoDAgkDAgkDAgkDAgkDAgcDAgcDAgkDAgkDAgcDAgkDAgcDAgMBAQMB - AQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9xXOdxYOttSM9tRMttRMtpPMNpO - LtpLK9pOLtlIJ9lJKdlJKdlIJ9hHJtFFJdNGJtVGJtNGJtVGJthHJtlIJ9VGJtpLK9lIJ9lIJ9lJKdlJ - KdhHJtpNLdpLK9NGJtlIJ9lJKdlIJ9hHJtpLK9pOLtpPMNtUNd1bPt1cP95iRt5gQ99lSt9kSOBqT+Br - UOJyWeN3XuN4YOR8ZeaFb+eMd+iPe+iRfeqZh+yike2ome6snfC3qvG8sPLBtvPHvfTJv/TLwvXRyfXN - xPXPxvPFu/LBtvG8sO+0p+2qm+yklOqZh+mTgOaHcuR9ZuJ1XOBrUN9mS91cP9xVN9tRMtlJKc9EJclC - JMJAI74/Irk9Ia86H6o4Hqc3Hpw0HJw0HJszHJIwGoUsGIAqF3koFnUnFXUnFW8lFGkjE2sjE2YhEmMh - El4fEV8fEVgdEFgdEFcdEFcdEFMcD1McD1IbD1AbD1AbD0sZDksZDkcYDUQWDEYYDUIWDD8VDEEWDD0U - Cz0UCzwUCzoUCzgSCjoUCzgSCjYSCjYSCjMRCTMRCTEQCTEQCS4QCSwPCCsOCCkOCCkOCCcNBycNBycN - ByQMByQMByQMByILBh8LBh0KBh0KBh8LBh8LBhsJBR0KBhsJBRoJBRgJBRYHBBgJBRgJBRYHBBQHBBMH - BBMHBBMHBBEGAxEGAxEGAxEGAxEGAw8FAxEGAxEGAw8FAw8FAw4FAw8FAw8FAw4FAwwFAwwFAwwFAwoD - AgoDAgoDAgkDAgkDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgMBAQMBAQIBAQMBAQMBAQMB - AQIBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9xXOdxYOtxXOdtRMttUNdtUNdtRMttSM9pLK9pLK9pO - LtlIJ9lJKdlJKdZGJtNGJtNGJthHJtZGJtFFJdNGJthHJtVGJtlJKdZGJtZGJtNGJtlIJ9pLK9pOLtpP - MNpNLdpOLtpOLtpOLttUNdxYOtxVN91bPt1aPN5iRt5gROBoTeBoTeBsUuFvVeJyWeJ1XOR8ZeR9ZuWD - beaJdOiPe+mVguqaiOyike2omfC2qfG8sPK/s/LDuPPHvfXPxvXPx/bTy/fa0/bUzPbVzvXPxvPHvfPF - uvK/s++zpe6un+yklOqciuiSfuaHceR9ZuN3XuBsUt9mS95gRNxYOtpOLtZGJtFFJcRBI74/Ir0/IrY8 - Ia05H6c3Hqg3Hp40HJszHJIwGosuGYErF3ooFnkoFnUnFXImFWkjE2gjE2QhEl8fEWEhElwfEVoeEFgd - EFoeEFcdEFUcD1IbD04aDk4aDk4aDk0aDkkYDUcYDUYYDUYYDUQWDEEWDD8VDD8VDD8VDDwUCzwUCzgS - CjoUCzYSCjYSCjUSCjMRCTEQCTAQCTAQCS4QCSwPCCkOCCkOCCkOCCkOCCcNByUNByQMByQMByQMByIL - Bh8LBh8LBiALBh8LBh0KBhsJBR0KBhoJBRoJBRoJBRgJBRgJBRYHBBQHBBYHBBQHBBQHBBQHBBEGAxEG - AxEGAxEGAxEGAxEGAw8FAxEGAw8FAxEGAw4FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoD - AgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgMBAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9xVN9xVN9tUNdpOLttUNdpPMNtRMttUNdtRMtpPMNpPMNpOLtpLK9pOLtlJ - KdZGJthHJtlIJ9lJKdNGJthHJtZGJthHJtZGJthHJtFFJdVGJtpLK9pNLdpOLtpLK9pOLttUNd1cP91a - PNxXOdxVN91cP95gQ9xYOt9kSN9kSOBrUOBrUOFwV+FvVeN3XuN4YOWCbOaJdOeMd+iRfeqZh+ufjuyi - ke2qm++zpfG8sPLDuPTJv/XPxvXRyfbVzvfZ0vfa0/jg2vjd1/jb1fbVzvbVzvXNxPPFuvLAtfC3qu+w - ouyklOqZh+iRfeaHcuR8ZeJyWeBsUt9kSN5gQ9tUNdpOLthHJsxDJMlCJL4/Irk9IbM7IKg3Hqg3HqA1 - HaI2HZcyG48wGoMsGHwpFnkoFnkoFnQnFW0kFGkjE2QhEmYhEmEhEl4fEVwfEVcdEFcdEFcdEFUcD1Uc - D1McD04aDk4aDksZDkcYDUkYDUcYDUQWDEIWDEEWDEEWDEEWDD8VDDoUCzwUCzoUCzYSCjYSCjUSCjMR - CTEQCTAQCTAQCTAQCSsOCCwPCCsOCCkOCCcNByUNByUNByUNByUNByQMByQMByILBiILBh8LBh8LBh0K - Bh8LBh8LBhsJBRoJBRoJBRgJBRgJBRYHBBYHBBQHBBMHBBMHBBQHBBMHBBEGAxEGAxEGAxEGAxEGAxEG - AxEGAw8FAw8FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgkDAgkDAgkDAgkD - AgkDAgkDAgkDAgcDAgcDAgcDAgUCAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////91aPNtRMttSM9pPMNtRMtpPMNtSM9pOLttRMtpNLdtRMtpOLtlJKdpOLtpNLdpLK9pNLdpOLtpL - K9lJKdVGJtlJKdlIJ9hHJtlJKdhHJthHJtlJKdlJKdpLK9pLK9pPMNtSM9xXOd1eQd1cP91eQeBoTd9l - St9mS99kSOBsUuBsUuFwV+J0W+N3XuWBauR/aOaHcueLdumWg+qaiOufjuymlu6uoO+zpfK/s/LDuPTL - wvXRyfbVzvjb1fjd1/jg2vni3fnk3/rm4fnk3/je2Pjb1ffZ0vXRyfTKwfLDuPC5rO6sneyikeufjuiP - e+aHcuR9ZuJyWeBrUN9lSt1aPNtSM9lIJ9FFJclCJMVBI70/IrM7IK05H6o4HqA1HZkyG5cyG5EwGogt - GIMsGHooFnooFnImFW0kFGgjE2gjE2MhEmMhEl4fEVoeEFcdEFcdEFcdEFMcD1McD1IbD04aDk0aDk0a - DkcYDUcYDUYYDUcYDUIWDEIWDD8VDD8VDDwUCzwUCzgSCjYSCjMRCTUSCjMRCTEQCTEQCS4QCS4QCTAQ - CSwPCC4QCSsOCCsOCCcNByUNByQMByUNByQMByQMByALBiALBh8LBiALBiALBh8LBh8LBh0KBhgJBRoJ - BRoJBRYHBBoJBRYHBBQHBBMHBBQHBBMHBBQHBBMHBBEGAxEGAxEGAw8FAxEGAxEGAxEGAxEGAw8FAw4F - Aw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgoDAgkDAgkDAgkD - AgkDAgkDAgUCAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9tSM9tSM9xV - N9tSM9tSM9tRMtpPMNpLK9pOLtpPMNpOLtpLK9pLK9lIJ9lJKdpLK9lIJ9pNLdtRMtpNLdpLK9lIJ9lI - J9lIJ9pNLdpLK9ZGJtpOLtpOLtpOLtpOLttRMttUNdxVN91bPt1aPN1eQeBqT+FwV+FvVeFwV+J1XOJy - WeN4YOR/aOWCbOWCbOeMd+iPe+mVguqZh+yklO2ome6uoO+0p/G8sPPFu/XPxvXRyfjb1fjd1/ni3frm - 4fvq5vvs6Pvs6Pvs6fvs6Prn4/rm4fnh3Pjb1fbVzvTLwvLDuPC3qu6uoOyklOqaiOiPe+aHceR/aOFv - VeBqT95gQ9xYOtpOLtpNLdFFJcRBI8A/IrY8Ia05H6o4HqM2HZ40HJszHJQxG4ouGX4qF3koFnkoFnUn - FXImFW0kFGgjE2QhEmEhEl4fEVoeEFoeEFcdEFUcD1UcD1McD1McD1AbD00aDk0aDkcYDUYYDUQWDEcY - DUIWDEIWDEEWDD0UCz0UCzoUCzYSCjUSCjMRCTMRCTMRCTEQCTEQCTAQCTEQCSwPCCwPCCwPCCwPCCsO - CCkOCCcNByUNBycNByQMByUNByQMByALBiALBh8LBh8LBh8LBh0KBhsJBRgJBRgJBRgJBRYHBBoJBRoJ - BRYHBBQHBBQHBBQHBBQHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAw8FAxEGAxEGAxEGAw4FAw4FAwwFAwwF - AwwFAw4FAwwFAwwFAwwFAwwFAwoDAgkDAgkDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgUCAQMB - AQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9tSM9tSM9xXOdxXOdpOLttRMttS - M9pOLtpOLtpOLtpLK9pOLtpLK9lJKdpLK9pLK9lJKdpNLdlIJ9pLK9ZGJtpNLdlJKdlJKdZGJtpOLtpO - LtpOLtpOLttRMtpOLttSM9xXOdxXOd1aPN5gRN5gROBrUOBrUOFuVOJyWeR8ZeWCbOR/aOaFb+aHcueL - dumTgOmVguuejOyjk+2ome6un/C3qvLAtfLDuPXPxvbUzPfa0/jg2vrn4/ro5Pvs6Pvu6/vu6/vu6/vu - 6/vu6/vu6/vs6fvq5vrn4/nh3PbVzvXPxvLBtvG6ru6uoOyklOqaiOeNeeaFb+R9ZuJyWd9mS95iRtxY - OtpOLtZGJsxDJMdCJL0/IrM7IK05H605H6M2HZ40HJEwGogtGIErF3koFnUnFXImFXImFW8lFGgjE2Mh - EmMhEmEhEloeEFwfEVoeEFUcD1McD1McD1McD04aDlAbD00aDk0aDkQWDEcYDUIWDEEWDEEWDDwUCzwU - CzwUCzYSCjMRCTYSCjUSCjMRCTMRCTEQCTEQCTAQCS4QCSwPCCwPCCwPCCwPCCkOCCkOCCcNByUNBycN - ByQMByQMByQMByILBiILBh8LBh8LBh0KBh8LBhsJBdbV1P////////////////////////////////// - /////////////////////////////////////////////+Dg39XU1L69vaalpGtpaAwFAw4FAwwFAwwF - AwwFAwwFAwwFAwoDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgcDAgkDAgUCAQMBAQMBAQMBAQMBAQMB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9tSM9xXOdtSM9tSM9tRMttRMtpOLtpNLdtSM9pPMNpO - LtpOLtpLK9pLK9pNLdVGJtlJKdlJKdhHJtVGJtpNLdlJKdhHJtlIJ9lIJ9lJKdpOLtpPMNpOLtpOLttS - M9xVN91bPt1aPN1eQd5iRuBqT+FwV+BrUOJyWeN3XuR/aOWCbOaHcueLduiPe+mVguqZh+yikeymlu2o - mO+wovC3qvK/s/PFu/XNxPfX0Pjd1/nh3Pro5Pvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vs6fro5Pje2PfZ0vXPxvLDuPC2qe2qm+yikeqZh+eNeeaFb+N4YOFwV99mS91eQdxXOdpOLtVGJs9E - JcdCJLs9IbM7IK86H6M2HZszHJkyG40uGYYsGHkoFnkoFnUnFXcnFW8lFGkjE2MhEmMhEl4fEV4fEVoe - EFwfEVcdEFcdEFUcD1McD1AbD04aDk4aDksZDkcYDUYYDUQWDEQWDD8VDDoUCzoUCzoUCzgSCjYSCjUS - CjUSCjUSCjMRCTEQCTAQCTEQCS4QCS4QCSsOCCwPCCwPCCkOCCkOCCkOCCcNByUNByQMByILBiILBiAL - BiALBh8LBh8LBh0KBh0KBhsJBdbV1P////////////////////////////////////////////////// - /////////////////////////////////////////////////////8rJyYqJiCwoJwoDAgwFAwoDAgoD - AgoDAgkDAgoDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgUCAQUCAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9tUNdtRMttRMttSM9pPMNpOLttSM9pOLtpNLdlJKdpLK9pLK9lJKdpNLdpN - LdlJKdpLK9lJKdpNLdlIJ9lIJ9VGJtpLK9lIJ9pOLtpOLtpOLtpPMNtSM9tUNdxVN91bPt1bPt5iRt9l - SuBrUOFwV+J1XOJ0W+JyWeR8ZeaHceaHcueLdumTgOmWg+uejOymlu6sne+ypO+zpfG8sPK/s/TJv/XP - xvfX0Pjd1/ni3frn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fvq5vnh - 3PfX0PTKwfLAte+0p+6sneyklOqciueNeeWCbON4YOFvVeBqT95gRNtUNdlIJ9NGJspDJMJAI7g9IbY8 - Iaw5H6I2HZszHJIwGoMsGH4qF3koFnQnFXImFXAlFG8lFGkjE2MhEmMhEmEhElwfEV8fEVwfEV8fEVgd - EFcdEFUcD1AbD00aDkkYDUYYDUQWDEIWDD8VDD0UCzoUCzgSCjgSCjgSCjYSCjUSCjMRCTMRCTMRCTAQ - CTAQCTAQCS4QCTAQCS4QCS4QCSwPCCkOCCcNByUNByUNBycNByILBiILBiQMByILBiALBiALBiALBh0K - Bh0KBh0KBtbV1P////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////X19aWkpC4qKAwFAwoDAgoDAgoDAgoDAgoD - AgoDAgkDAgcDAgcDAgcDAgkDAgUCAQMBAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9tUNdtSM9tUNdtRMttRMtpOLtpPMNtRMtpOLtlIJ9pOLtpNLdlJKdpLK9pNLdpNLdpNLdpOLtlJ - KdlJKdpLK9NGJtpLK9pNLdpLK9pPMNpOLttSM9xVN9tSM91bPt1bPt9lSt9mS+BrUOBrUOJ1XON7Y+R/ - aOaFb+aJdOiSfuiSfuiSfuqaiOuejOyklO2qm++wovC3qvG9sfLDuPTKwfXPxvbVzvjd1/ni3frn4/vs - 6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6frm4fjg2vbTy/TKwfLA - tfC2qe2qm+yjk+mXheeMd+aFb+N7Y+FvVeBqT95gQ9xXOdpOLtZGJs9EJcpDJL0/IrQ7IKw5H6U3Hpsz - HIsuGYYsGIMsGH4qF3cnFXcnFXQnFXImFWkjE2kjE2gjE2MhEmEhElwfEVoeEFgdEFgdEFgdEFUcD0sZ - DkcYDUQWDEQWDD8VDEEWDDwUCzwUCzgSCjgSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTEQCTAQCTAQCTAQ - CSwPCCsOCCwPCCsOCCcNByUNByUNByUNByUNByILBiILBiALBiALBiALBh8LBh8LBhsJBRsJBdbV1P// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////X19aWkpAoDAgoDAgoDAgkDAgoDAgoDAgkDAgcDAgkD - AgcDAgcDAgUCAQMBAQMBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9xXOdxXOdpP - MNpPMNpOLttSM9tRMttSM9pOLtpOLtpLK9lJKdhHJttRMtpNLdlJKdpLK9pLK9pOLtlJKdpOLtlJKdlJ - KdpNLdpPMNpPMNpPMNtUNd1aPN9lSt5gQ99kSOBoTeBrUOJ1XON4YOWDbeWBauaHceaJdOiRfemXheuf - juyikeyklO2ome6sne+wou+0p/G6rvLDuPTJv/XPx/bVzvjb1fjg2vrm4fvs6Pvu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Pnk3/jb1fXRyfPHvfG8sO+ypO2omeyi - kemVgueMd+aHceR8ZeFwV+BrUN9lSt1bPttUNdpOLtlJKc5EJcdCJL0/Irs9IbE7IKA1HZkyG5EwGo0u - GYErF34qF3koFnQnFW0kFGsjE2YhEmEhEloeEFwfEVgdEFgdEFUcD1McD1AbD0cYDUYYDUIWDEIWDEEW - DDwUCzwUCzwUCzYSCjYSCjYSCjUSCjMRCTMRCTMRCTMRCTEQCTEQCTEQCTAQCS4QCSkOCCkOCCwPCCwP - CCsOCCcNByUNByQMByILBiQMByQMByALBh8LBh8LBh8LBh8LBh0KBh8LBtbV1P////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////+Df30VBQAoDAgkDAgoDAgkDAgkDAgcDAgcDAgcDAgcDAgUCAQUC - AQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pPMNtSM9tUNdtRMtpPMNpOLttS - M9tRMtpOLttRMtpOLtpNLdpLK9tRMtpOLtpLK9pOLtpNLdpNLdlJKdlJKdlJKdpOLtpNLdtRMttSM9xX - Od5gQ95gROBrUOBrUOFvVeJ1XON7Y+WBauWCbOaHcueLdueNeeiPe+qZh+uejOyjk+ymlu2ome6uoPC2 - qfC3qvLBtvLDuPPHvfXNxPbTy/fX0Pje2Pnk3/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fvq5vnh3PfX0PTLwvLDuPC5rO+wou2omOugj+qZh+iPe+aH - cuWCbON7Y+J1XOFwV+BrUN9lSt1eQdxXOdtRMtlIJ85EJcdCJLk9Iaw5H5cyG5IwGoUsGIAqF3koFnAl - FGgjE2YhEmEhEl8fEVwfEVcdEFUcD1UcD1IbD1AbD0sZDkcYDUYYDUcYDUIWDEEWDD0UCzwUCzgSCjgS - CjgSCjYSCjUSCjMRCTMRCTMRCTMRCTMRCTAQCS4QCTAQCS4QCSwPCCwPCCkOCCkOCCkOCCkOCCcNByUN - ByILBiQMByILBiALBh8LBiALBh0KBiALBhsJBR0KBtbV1P////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////X19VlWVQkDAgkDAgkDAgkDAgkDAgkDAgcDAgcDAgMBAQUCAQMBAQIBAQIBAQMB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9tSM9tSM9pPMNxVN9pPMNpNLdpOLttRMtpNLdpPMNpN - LdpNLdpNLdhHJtpNLdpLK9lIJ9pOLtpOLtlJKdpOLtlJKdtUNdpOLtxYOt1eQd9lSt9lSuFwV+N3XuN5 - YeR9ZuWBauaHcuaHcueMd+iSfumVguqaiOuejOyjk+ymluymlu6sne6un++zpfC3qvG8sPLAtfPHvfTL - wvXRyffX0Pfa0/jg2vrm4fvs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vs6Prm4fjb1fXRyfTJv/LAtfG6ru+wou2omeyklOuejOmWg+iRfeeLduaHcuR/ - aON3XuJyWeBrUN9mS95gRNxYOtpOLthHJslCJL0/IrE7IKA1HZQxG4YsGHkoFmsjE2QhEmQhEl4fEVwf - EVcdEFMcD1McD1McD1IbD04aDk4aDkcYDUYYDUIWDEIWDD8VDEEWDD0UCz0UCzgSCjgSCjYSCjYSCjMR - CTMRCTEQCTEQCTAQCTEQCS4QCS4QCS4QCSwPCC4QCSsOCCkOCCcNBycNBycNByQMByALBiILBiQMByIL - BiALBh8LBh8LBh8LBhsJBRsJBdbV1P////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////X19VhWVQkDAgkDAgkDAgkDAgkDAgkDAgkDAgMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQAA - AAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9tSM9tRMttRMttSM9pPMNpPMNpOLttSM9pLK9pLK9pOLtpLK9pNLdpOLtpO - LtpNLdpLK9pOLtpNLdpNLdpOLtpPMNtRMt1bPuBoTeBsUuJyWeN7Y+WDbeaFb+aHceeMd+iPe+iSfumT - gOmXheqciuuejOyikeymluymlu6sne6uoO+wovC2qfC5rPG8sPLBtvPFuvPHvfTLwvXRyffX0Pjb1frm - 4frn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vs6fro5Pjg2vjb1fXPxvPFu/LBtvG6ru+ypO2omeyklOqciumWg+iRfeeLduaHceR9ZuN4YOJ0W+Fw - V+BrUN9kSN5gQ9xYOtpOLtlIJ85EJcA/IrQ7IJ40HIouGXkoFm0kFGYhEl8fEVwfEVoeEFgdEFcdEFcd - EFMcD1AbD0sZDkkYDUQWDEIWDD8VDEIWDEEWDD8VDDgSCjgSCjgSCjgSCjYSCjUSCjMRCTMRCTAQCTAQ - CS4QCSwPCCwPCCwPCCwPCCwPCCsOCCkOCCUNByUNByUNByQMByILBiALBiILBiILBh8LBh0KBh0KBh0K - Bh0KBhsJBdbV1P////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////X19VhW - VQkDAgkDAgkDAgkDAgkDAgcDAgUCAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9tSM9tSM9tSM9tRMttUNdtRMtpPMNpOLtpOLtpNLdpOLtpNLdpPMNpOLtpPMNpLK9pNLdpOLtlJ - KdpOLtpOLttSM91cP+BsUuR8ZeWCbOaHcueLdueNeemTgOmVgumXhemWg+qciuufjuufjuyjk+yjk+yk - lO6un+6sne6uoO+wou+0p/C2qfG8sPK/s/LDuPPFuvTKwfXPxvbUzPfZ0vjd1/nh3Pro5Pvs6fvu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vq5vnh3Pfa - 0/XRyfTKwfLBtvG6ru+zpe6un+2omOyklOuejOmXhemVguiPe+aJdOaHceWCbON7Y+N5YeN3XuFvVeBr - UN5iRt1cP9xYOtpPMNlJKcpDJLM7IJszHHcnFWgjE2MhEl8fEVoeEFgdEFUcD1AbD00aDksZDkcYDUcY - DUQWDEIWDD8VDEEWDD0UCzwUCzgSCjgSCjYSCjYSCjUSCjMRCTMRCTMRCTEQCTAQCTAQCSwPCC4QCSsO - CCsOCCsOCCkOCCkOCCcNByUNByQMByUNByILBiILBiQMByALBiALBh8LBh0KBh0KBhsJBR0KBtbV1P// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////X19SwoJwoDAgkDAgkD - AgkDAgkDAgMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9tRMttSM9tS - M9tSM9pOLtpPMNpPMNpPMNpLK9pLK9pOLtpNLdpOLtpOLtpNLdpOLtpNLdpOLtpOLttRMtpLK95iRuJy - WeaFb+eMd+iRfemTgOmWg+mXheqaiOqaiOuejOqciuugj+yikeyike2omOyklO6un+6un+6uoO+ypO+0 - p/C2qfC5rPG9sfLBtvLDuPPFu/TLwvXPx/bUzPfa0/jg2vni3fvq5vvs6Pvu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vq5vjg2vbVzvXNxPPHvfLAtfC5 - rO+zpe6sneyklOyikeuejOqZh+mTgOiPe+eLduaHcuWCbOR9ZuN5YeJ1XOJyWeFuVOBoTd5iRt5gRN1b - PtxXOdpPMNhHJr0/IpEwGmgjE1oeEFgdEFcdEFcdEFIbD04aDksZDkcYDUkYDUQWDEYYDUIWDD8VDEIW - DEEWDDoUCzoUCzgSCjYSCjoUCzUSCjUSCjMRCTMRCTEQCTAQCTAQCSwPCCwPCCwPCCcNBykOCCkOCCkO - CCkOCCUNByUNByUNByILBiILBh8LBh8LBh8LBiALBh0KBhsJBRsJBRoJBdbV1P////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////9XU1AoDAgkDAgkDAgcDAgcDAgMBAQMB - AQMBAQMBAQIBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9tRMttRMttRMttRMtpPMNtRMtpN - LdpOLtpOLtpOLtpOLtlJKdpNLdpNLdpOLtpLK9pNLdpOLttRMtpOLttSM91eQeN4YOaHcuiPe+iSfumV - gumWg+mXheqZh+qaiOugj+ufjuyikeyklOyklO2ome2ome6sne6uoO+wou+ypO+zpfC2qfG9sfG9sfLD - uPLDuPPHvfTLwvXPxvbVzvfa0/jg2vni3fvq5vvs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fro5Pjd1/bUzPTKwfLDuPLAtfC2qe6uoO2omeyjk+ue - jOqaiOmTgOeNeeaJdOaFb+WCbON7Y+N5YeN5YeJyWeFuVOBrUN5iRt1eQd1eQdxYOttUNdpOLtFFJb0/ - IpYyG2sjE1gdEFUcD1UcD1McD04aDk0aDk0aDksZDksZDkcYDUYYDUQWDEEWDD8VDD0UCzoUCzoUCzgS - CjYSCjgSCjMRCTMRCTEQCTMRCTMRCTAQCTAQCS4QCSwPCCwPCCsOCCsOCCkOCCkOCCUNBycNByUNByQM - ByQMByQMBx8LBh0KBh8LBh0KBh0KBhsJBRoJBRoJBdbV1P////////////////////////////////// - /////21qaREGAxEGAxEGAxEGAxEGAw8FAw4FAw8FA1pXVnx6ebKxsOvq6v////////////////////// - /////////////////////////////////////5iXlgkDAgcDAgcDAgcDAgMBAQMBAQMBAQMBAQMBAQIB - AQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9tRMtpPMNpOLttSM9pOLtpPMNtRMtpOLttRMtpLK9pP - MNpLK9pOLtpLK9tRMtlIJ9pOLttRMtpPMNpPMNpPMNtUNd5gROFuVOR8ZeaHcuaHcueLduiPe+mWg+mW - g+qZh+qaiOufjuugj+yjk+2omO2omO2ome2qm++wou+wou+ypO+zpfG6rvLAtfLAtfPFuvPFu/TLwvXR - yfbUzPfZ0vje2Pnh3Prn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vs6fni3fjb1fbTy/TJv/LDuPG9se+0p+6un+ymluyikeqaiOmVguiPe+aJdOWD - beWCbON7Y+N3XuJyWeFwV+BrUN9mS95iRt1bPtxVN9pOLtlIJ8xDJL4/Iqo4Ho8wGnAlFFwfEVcdEFcd - EFIbD1AbD04aDk4aDk4aDkkYDUQWDEcYDUQWDEEWDDwUCzwUCz0UCzgSCjoUCzYSCjYSCjUSCjMRCTYS - CjMRCTMRCTEQCTAQCTAQCS4QCSwPCCwPCCwPCCsOCCcNBycNByUNBycNByQMByILBiQMByQMByILBiAL - Bh8LBh0KBh8LBhsJBRoJBRoJBdbV1P///////////////////////////////////////2xpaBEGAxEG - AxEGAxEGAxEGAw8FAw8FAw4FAw4FAw4FAw4FAw4FA3x6eevq6v////////////////////////////// - //////////////////////X19SwoJwkDAgcDAgcDAgMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAA - AAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9pPMNtRMttUNdtSM9pPMNtSM9pOLtpNLdpLK9pOLtlJKdpOLtpLK9pOLtpN - LdpOLttRMtpOLttSM9tSM9pPMNtSM9xVN91eQd9mS+FwV+J0W+R9ZuaHcuaHceaJdOiRfeiRfemTgOmX - heuejOyjk+ugj+yklOymlu2ome+wou+wovC2qfC5rPG9sfG9sfLDuPPFuvTJv/XNxPbTy/fZ0vje2Pnh - 3Prn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vq5vnh3PfZ0vXRyfPHvfK/s/C3qu+wou2qm+yjk+qaiOmVguiRfeaJdOWDbeR/aON7Y+J1XOBsUuBq - T99kSN1bPtxYOtpOLthHJs5EJcJAI7Y8IaM2HZcyG4MsGG8lFGEhElwfEVcdEFIbD1IbD00aDk0aDksZ - Dk0aDk0aDkkYDUYYDUIWDEQWDD8VDD8VDDwUCzwUCzoUCzYSCjUSCjYSCjMRCTMRCTEQCTEQCTAQCS4Q - CS4QCSwPCCwPCCwPCCwPCCkOCCcNBycNBycNByQMByQMByILBiILBiILBiILBiALBh8LBh8LBh0KBhoJ - BRgJBRsJBdbV1P///////////////////////////////////////2xpaBEGAxEGAxEGAxEGAxEGAxEG - AxEGAw8FAw4FAw4FAw4FAw8FAw4FAw4FA7KxsP////////////////////////////////////////// - /////////6WkpAcDAgcDAgcDAgMBAQMBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9tSM9pOLtpNLdtRMtpOLtpOLtpOLtpNLdpOLtpNLdpOLtpLK9pPMNpNLdpLK9pOLtpPMNpOLttR - MttRMttSM9tRMtxXOd1bPt5gQ99lSt9mS+FwV+J0W+N3XuWBauaFb+aHcuaJdOiPe+mTgOmXheqaiOqc - iuyikeyjk+2qm+6sne+ypO+zpfC5rPG6rvK/s/LDuPPFu/TKwfbTy/fX0Pje2Pjg2vrm4fvq5vvs6fvu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Prm4fjd1/bUzPTK - wfPFuvG6ru+0p+6un+yklOqciumVgueNeeaHcuWCbOR8ZeJ0W+BsUuBrUN9kSN1bPttRMtpOLs9EJclC - JL0/IrE7IKA1HZYyG4gtGHkoFm0kFGQhEmMhElwfEVcdEFUcD1McD1McD00aDksZDk0aDk0aDkcYDUQW - DEQWDD8VDD8VDD0UCz0UCzoUCzgSCjgSCjUSCjUSCjMRCTMRCTMRCTAQCTAQCTEQCTAQCSwPCCwPCCwP - CCsOCCsOCCcNBycNByUNByUNByUNByQMByQMByALBh8LBiALBiALBh8LBh8LBhsJBRoJBRgJBdbV1P// - /////////////////////////////////////2xpaBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw4F - Aw4FAw4FAw4FAw4FAwwFA4qJiP////////////////////////////////////////////////X19Sso - JwcDAgcDAgMBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9pPMNtRMtpO - LtpPMNpOLtpOLtpOLtpPMNpOLtpPMNpOLtpPMNtRMtpPMNpNLdpLK9pOLtpOLttRMttRMttUNdxXOdxX - Od1aPNxYOt1aPN1bPt9kSN9mS+BrUOFvVeN4YOR8ZeWBauWDbeaFb+eNeeiRfemWg+qciuufjuyklO2o - mO2omO6uoO+wovC3qvK/s/K/s/LDuPTKwfXPxvXRyffa0/je2Pni3fro5Pvs6Pvu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vq5vjg2vfa0/XPxvPFuvG9se+0p+2qm+yk - lOufjumWg+eLduWCbOR9ZuN3XuBrUN9mS91eQd1aPNtRMtpNLdVGJslCJL0/IrE7IKU3HpkyG4ouGX4q - F3cnFXAlFGQhEmEhEl4fEVwfEVcdEFMcD1AbD1McD04aDksZDkkYDUcYDUkYDUIWDEQWDEQWDD8VDD0U - CzoUCzoUCzgSCjYSCjMRCTMRCTMRCTMRCTEQCTEQCTAQCS4QCSwPCCwPCCwPCC4QCSwPCCkOCCUNBykO - CCUNByQMByQMByQMByQMByALBiALBh8LBiALBh8LBhsJBR0KBh0KBhsJBdbV1P////////////////// - /////////////////////2xpaBEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4F - AwwFAwwFA7GwsP///////////////////////////////////////////////4qIiAkDAgUCAQIBAQIB - AQIBAQIBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9tRMtpNLdpOLtpOLtpLK9pOLtpP - MNpNLdpNLdpOLtlJKdpOLtpOLtpNLdpOLtpLK9pOLttRMttUNdpPMNtSM9tUNdxVN9tRMttRMt1bPt1a - PN5gQ99kSN5gRN9mS+FuVOJ0W+N4YON5YeR9ZuaHcuaHcueMd+eNeeiSfuugj+yjk+yklO2omO+wou+y - pPC2qfG8sPG9sfPFuvPHvfXPxvfX0Pje2Pjg2vrn4/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vu6/vu6/vu6/vu6/vu6/vs6Pni3fje2PbTy/TJv/LAte+0p+2qm+yjk+qciuiRfeaJdOWCbON5 - YeJyWeBqT95gRNtUNdpOLtlJKdFFJcpDJL4/IrE7IKo4HqA1HZcyG40uGYErF3koFnAlFGkjE2MhEl4f - EVwfEVoeEFcdEFAbD04aDlAbD04aDksZDkkYDUYYDUYYDUEWDEIWDEEWDEEWDD0UCzwUCzgSCjoUCzUS - CjMRCTMRCTMRCTMRCTMRCTMRCTEQCTEQCS4QCSsOCCwPCCsOCCsOCCkOCCkOCCcNByUNByQMByQMByQM - ByQMByALBiILBiALBiALBh0KBhsJBRoJBRsJBRgJBdbV1P////////////////////////////////// - /////2xpaBEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAxEGAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwwFA+Dg - 3////////////////////////////////////////////8nJyQcDAgUCAQMBAQMBAQMBAQMBAQIBAQMB - AQIBAQMBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9tRMtpNLdpOLtpLK9pOLtpPMNpOLtpOLtpLK9pOLtpO - LtpOLtpOLthHJtlIJ9pLK9pOLttRMtpPMNtUNdtRMttUNdtUNdpPMNxVN91cP95gRN5gRN5iRt5gQ99m - S+BrUOBrUOJyWeN3XuN4YON5YeWBauaHceeMd+eNeemWg+uejOyjk+2omO2ome2qm+6uoPC2qfG6rvLD - uPLDuPTKwfXPxvbUzPjd1/nk3/vs6Pvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vu - 6/vu6/vq5vni3fje2PbVzvTLwvLAtfG6ru6un+yjk+qaiOiRfeaHcuWBauJ1XOFuVN5iRt1cP9tUNdpN - LdhHJs5EJb4/Irg9Iac3Hp40HJszHIsuGYMsGH4qF3UnFXQnFW0kFGgjE2QhEmEhEl4fEVoeEFgdEFMc - D1IbD04aDk0aDkkYDUkYDUQWDEYYDUEWDEEWDD0UC0IWDD0UCzwUCzoUCzwUCzgSCjUSCjUSCjEQCTEQ - CTEQCTEQCTEQCTAQCS4QCSsOCCwPCCkOCCcNBykOCCcNBykOCCUNByQMByQMByILBiILBiALBiALBiAL - BiALBh8LBhsJBRsJBRsJBRoJBdbV1P///////////////////////////////////////2xpaBEGAxEG - AxEGAw8FAxEGAxEGAw8FAw8FAw8FAw8FAw4FAw4FAw4FAwwFAw4FAwwFAwoDAmtpaP////////////// - /////////////////////////////////ysoJwUCAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9tRMtpNLdpLK9pOLttRMtpOLtpOLttSM9pNLdpOLtpNLdlJKdpOLtpOLtpN - LdpNLdpLK9pPMNtRMtxVN9xXOdtUNdxXOdxVN91eQd1bPt1aPN1cP95gQ95gQ95iRuBoTeBoTeFuVOJy - WeN3XuN5YeWBauWBauaHcuiPe+mXhemWg+mWg+ufjuyjk+ymluymlu6uoPC3qvG9sfK/s/LDuPTJv/XR - yffX0Pjg2vrn4/vs6Pvs6Pvs6Pvs6fvu6/vu6/vu6/vu6/vu6/vu6/vu6/vu6/vs6fvq5vrm4fjd1/bU - zPTLwvLDuPC5rO6uoOyklOuejOiPe+aHcuR8ZeJ1XOBrUN9lStxYOttRMtlIJ89EJcxDJMRBI7s9IbM7 - IKM2HZcyG5EwGoYsGHwpFnkoFnUnFWkjE20kFGYhEl8fEV8fEV8fEV4fEVwfEVcdEFcdEFIbD04aDk0a - DkkYDUcYDUYYDUEWDEIWDD0UCzwUCzoUCzoUCzoUCzgSCjMRCTUSCjMRCTMRCTEQCTMRCTAQCS4QCTAQ - CTAQCSsOCCkOCCsOCCcNBycNBycNBycNByQMByILBiILBiILBiALBiALBh8LBh8LBh0KBhsJBRsJBR0K - BhoJBRgJBdbV1P///////////////////////////////////////2xpaBEGAxEGAxEGAw8FAw8FAw8F - Aw8FAw8FAw8FAw8FAw4FAw8FAw4FAw4FAwwFAwwFAwoDAgwFA769vf////////////////////////// - /////////////////4mIiAMBAQMBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9lJKdpNLdpOLtpOLtpOLtpPMNpPMNpPMNpOLtlJKdlIJ9pLK9pOLtpPMNlIJ9pLK9pOLtpPMNtS - M9xYOttUNdxXOd1bPt1aPNxXOd1aPN1aPN5iRt1cP95gQ91eQd9lSt9mS+BrUOFuVON3XuN7Y+WDbeWD - beaHceaHcueMd+mTgOiRfemVguqaiOugj+yjk+ymlu6un++zpfC3qvK/s/PHvfXRyffX0Pfa0/jd1/rm - 4fro5Pro5Pvq5vvs6Pvs6fvs6fvu6/vs6fvu6/vu6/vs6Pro5Pnk3/jg2vfX0PTLwvLDuPG6ru+ypOyk - lOqaiOiSfuaHcuR8ZeJyWeBqT95iRtxYOtpPMNZGJspDJMdCJL0/Irg9Ia86H6M2HZszHJYyG48wGoUs - GHkoFnImFXAlFG0kFGYhEmMhEmQhEl4fEVoeEFoeEFgdEFMcD1UcD1IbD1IbD04aDk0aDkkYDUcYDUYY - DUIWDD0UCz0UCzoUCzgSCjgSCjgSCjMRCTYSCjMRCTMRCTAQCTEQCTEQCS4QCSwPCCwPCCwPCCsOCCsO - CCsOCCkOCCcNByUNByUNByILBiILBiQMByALBh8LBh8LBh0KBh0KBhsJBRsJBRoJBRoJBRgJBdbV1P// - /////////////////////////////////////2xpaBEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8F - Aw4FAw4FAw4FAwwFAw4FAw4FAwwFAwwFA2tpaP////////////////////////////////////////// - /729vQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9pOLtpNLdpL - K9pOLtpLK9pOLtpLK9pOLtpLK9pNLdpNLdpOLtpOLtpNLdpNLdpOLttRMttSM9tUNdtSM9tUNdxXOdxX - Od1bPt1aPNxYOt1bPt1aPN5gQ99kSN5gRN5gRN9mS+BqT+BsUuN3XuN5YeR9ZuWBauWCbOWBauaHceiP - e+mVgumTgOmWg+qaiOqciuyike6sne6sne+ypO+0p/PFu/bTy/XRyfbUzPfZ0vjd1/jg2vnk3/rm4fro - 5Pro5Pvq5vvs6Pvq5vro5Pvq5vrm4fjg2vjb1fbVzvXPxvPFu/G6ru+wouymluqaiOiRfeaHcuR/aOJy - WeBqT99kSN1aPNpOLtZGJs9EJcVBI70/IrY8Ia05H6I2HZszHJYyG5IwGoouGYgtGHkoFnUnFXImFWsj - E2YhEmMhEmEhEl8fEVoeEFcdEFUcD1IbD1McD1AbD04aDk0aDkcYDUcYDUkYDUYYDUQWDD8VDD8VDD0U - CzwUCzwUCzYSCjMRCTYSCjMRCTEQCTAQCTEQCTAQCS4QCS4QCSwPCCsOCCwPCCwPCCsOCCkOCCcNByUN - ByQMByILBiALBiILBiILBiALBiALBh8LBh8LBhsJBRoJBRoJBRoJBRgJBdbV1P////////////////// - /////////////////////2xpaBEGAxEGAxEGAxEGAw8FAxEGAw8FAw8FAw4FAw8FAw4FAw4FAwwFAwwF - AwwFAwwFAwwFAwwFAwwFA+vq6v///////////////////////////////////////+Df3wMBAQMBAQIB - AQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pNLdpLK9pOLtlJKdpOLtpNLdpO - LtlJKdpOLtpLK9pNLdpNLdtRMttRMtpOLttRMtpPMNxYOtpOLtpPMNxVN9xYOtxXOd1aPN1bPt1aPN1b - Pt5gRN1cP95iRt9kSOBoTeBqT+BqT+BrUOFuVOFwV+N7Y+R/aOWDbeR/aOWDbeeNeeiRfemVguiPe+mV - guqciuufjuyklO2omO2ome+ypPG9sfLDuPTLwvXNxPXRyffX0Pjb1fje2Pjg2vnh3Pnk3/ni3fnk3/nk - 3/nk3/jg2vje2PfX0PbTy/XPxvPFu/G6ru+wouymluqciuiSfuaHcuWCbON3XuBrUN5gQ91bPttUNdlJ - KdNGJsdCJL4/IrQ7IK86H6o4Hpw0HJcyG5cyG4ouGYUsGH4qF3cnFXkoFnQnFW8lFGYhEmQhEl8fEVoe - EFgdEFgdEFUcD1McD1AbD04aDksZDkkYDUYYDUYYDUYYDT8VDEIWDEEWDDoUCzoUCzwUCz0UCzoUCzoU - CzYSCjUSCjMRCTMRCTMRCTEQCS4QCSwPCC4QCSwPCCsOCCsOCCsOCCcNByUNByUNByQMByQMByILBiIL - BiILBiALBiALBh8LBh0KBhsJBR0KBhsJBRoJBRgJBdbV1P////////////////////////////////// - /////2xpaBEGAxEGAxEGAxEGAw8FAxEGAw8FAw8FAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwoDAgoD - AgoDArGwsP///////////////////////////////////////////wMBAQMBAQMBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9pLK9pNLdpOLtpLK9lJKdpNLdpNLdpLK9lJKdpLK9lJ - KdpOLttUNdlIJ9pOLttRMttSM9pOLtxYOtxVN9xXOdxXOdxYOtxYOt1aPNxYOt1cP9xXOd1cP91cP99k - SN9kSN9lSuBrUOBrUOFwV+J0W+N5YeN7Y+WBauR/aOeLduiPe+iPe+iSfuiRfemTgOqZh+qaiOuejOyk - lO2ome6sne+zpfG6rvLAtfLDuPTKwfTLwvbUzPfX0Pfa0/je2Pje2Pjg2vje2Pjg2vjg2vjd1/bVzvXP - xvTJv/PFuvC5rO6uoO2omOqciumVguaHcuR9ZuN3XuFuVN9lStxXOdxXOdpLK9ZGJslCJMJAI70/IrM7 - IKo4HqI2HZszHJYyG5IwGoouGYAqF3wpFnUnFXUnFXAlFG0kFG0kFGgjE2YhEl4fEVgdEFgdEFUcD1Ib - D04aDksZDkcYDUYYDUcYDUYYDUIWDEEWDD0UCzwUCzgSCjwUCzoUCzwUCzgSCjYSCjUSCjYSCjgSCjMR - CTMRCTMRCTEQCS4QCSwPCCkOCCsOCCsOCCsOCCkOCCUNByUNByQMByILBiQMByQMByALBiALBh8LBh0K - BhsJBRoJBRsJBRsJBRgJBRgJBdbV1P///////////////////////////////////////2xpaBEGAxEG - AxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw4FAw4FAw4FAw4FAw4FAwwFAwoDAgoDAgwFA3t5eP////// - /////////////////////////////////////0FAQAMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9pOLtpOLtpOLtpNLdlJKdpOLtlJKdpOLtpLK9pOLtpNLdpNLdpOLttSM9pO - LtpOLttSM9tRMttUNdxYOtxXOdxVN9xXOd1aPN1aPN1aPN1cP91bPt5gQ91cP99lSt5iRuBoTeBqT+Fv - VeJ0W+J1XON7Y+WBauaHceaHceeMd+aJdOeMd+mTgOmTgOmTgOmWg+qaiOqciuuejOyikeymlu2qm++w - ovC3qvG9sfLDuPPFu/XNxPXRyfbUzPbVzvfZ0vfZ0vfZ0vfa0/fa0/bVzvXPx/TJv/LBtvG8sO+ypO2o - meufjumWg+aJdOWBauJ1XOBsUuBqT91eQdtSM9pNLdFFJc9EJcVBI70/IrY8IbE7IKg3Hpw0HJQxG5Qx - G40uGYYsGH4qF3cnFXImFW0kFGgjE2YhEmQhEl8fEWEhEmEhEmEhElwfEVcdEFIbD1IbD04aDkcYDUcY - DUcYDUcYDUQWDD0UCz8VDD0UCzoUCzwUCzgSCjUSCjMRCTYSCjMRCTEQCTMRCTEQCTEQCTMRCTMRCTMR - CTMRCSwPCCsOCCkOCCsOCCsOCCUNByUNByUNByQMByQMByILBiALBh8LBh0KBh8LBh0KBhoJBR0KBh0K - BhoJBRgJBdbV1P///////////////////////////////////////2xpaBEGAxEGAxEGAxEGAxEGAw8F - AxEGAxEGAw8FAw4FAw8FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFA0VCQf////////////////////// - /////////////////////2hnZwMBAQIBAQMBAQIBAQIBAQMBAQMBAQIBAQAAAAIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9pOLtpLK9pLK9lJKdlJKdlIJ9lJKdlJKdpLK9pOLtpOLtpPMNpOLtpPMNpOLttUNdpOLtxVN9xX - OdtUNdxYOtxYOt1eQd5gRN5iRt1eQd9mS99mS+BoTeBrUOBrUOBqT+FvVeFuVOFvVeFvVeJyWeN3XuN7 - Y+WBauaHcuiPe+aHcuiSfumTgOmTgOmVgumXhemXhemXheqaiOufjuyklOyklO6sne6uoPC2qfG6rvK/ - s/LDuPPHvfXNxPXRyfbTy/bTy/bUzPbUzPbUzPXPxvPHvfLDuPG6ru+0p+2omOufjumWg+eMd+WDbeN3 - XuFuVN9mS91eQdxVN9tRMtlIJ9FFJcpDJMJAI70/IrM7ILE7IKI2HZszHJYyG4ouGYYsGIAqF3ooFnQn - FXImFXAlFGkjE2kjE2YhEmEhEl4fEV4fEVgdEFoeEFgdEFcdEFUcD04aDkkYDUkYDUQWDEEWDEIWDD8V - DD0UCz0UCzoUCzoUCzoUCzgSCjYSCjUSCjMRCTAQCTAQCTAQCS4QCTEQCS4QCS4QCSwPCCwPCC4QCS4Q - CS4QCSsOCCwPCCcNBycNByQMByALBiALBh8LBiALBiALBh0KBh0KBhoJBRoJBRoJBRoJBRoJBdbV1P// - /////////////////////////////////////2xpaBEGAxEGAxEGAxEGAxEGAxEGAw8FAw4FAw4FAw8F - Aw8FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAv////////////////////////////////////// - /////3l4eAMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lJKdpOLtlJ - KdpOLtpLK9pNLdlIJ9hHJthHJtpNLdpOLtpNLdpPMNpPMNpPMNpOLtpPMNpPMNxXOdxXOd5gQ99kSN9l - St9mS+BqT+BrUN9kSN9mS+BrUOFuVOFwV+JyWeJ0W+JyWeN5YeJyWeJ1XOJ1XON5YeR/aOWBaueLduaH - cumTgOmWg+qZh+qaiOqaiOmVgumXhemXheqciuuejOugj+yjk+ymlu6uoO+0p/C3qvG8sPLBtvPFu/TL - wvTLwvXPxvXNxPXNxPXNxPTKwfLDuPG6ru+0p+yjk+yjk+mWg+eMd+WDbeN7Y+FuVN9mS95iRt1bPtpO - LthHJtZGJslCJMVBI7s9IbM7IK86H6c3HqM2HZYyG5EwGosuGYYsGHkoFnUnFXImFW8lFHAlFGsjE2Yh - EmEhEl8fEV4fEVoeEFcdEFcdEFUcD1AbD1IbD1AbD04aDksZDkkYDUQWDEEWDDwUC0QWDD0UCzgSCjYS - CjYSCjUSCjYSCjYSCjUSCjEQCTEQCTAQCS4QCS4QCSwPCCsOCCsOCCkOCCkOCCkOCCsOCCcNBycNBycN - BycNBykOCCQMByILBiILBiILBiALBh8LBhsJBRoJBRoJBRgJBRgJBRgJBdbV1P////////////////// - /////////////////////2xpaBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8FAw4FAw4FAwwF - AwwFAwwFAwwFAwwFAwoDAgoDAurq6v///////////////////////////////////////6SkpAMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pLK9lIJ9lJKdlIJ9VGJthHJtZG - JtpLK9pPMNpOLtpOLtpLK9pNLdpPMNpOLtpNLdtUNdxVN95gRN9kSN9lSt9mS+BoTd9lSuBrUOBoTd9m - S+BrUOFvVeBsUuFwV+N3XuJyWeJ0W+J1XOR/aOJ1XOJ1XOJ0W+R9ZuR9ZuaJdOaJdOeMd+iSfumTgOqZ - h+qZh+mWg+mTgOmVguqciumXheqZh+ugj+yjk+2omO6uoO+ypPC2qfG8sPK/s/PFuvPFuvPFu/PHvfPH - vfPFuvLDuPK/s++zpe6sneugj+iSfueLduWBauN5YeFwV+BqT95iRt1bPttUNdlJKdNGJsxDJMJAI74/ - IrQ7ILM7IKw5H6I2HZkyG5YyG48wGoouGYgtGHkoFncnFXAlFG0kFHAlFGsjE2YhEl8fEVwfEVwfEVoe - EFUcD1cdEFIbD0sZDk0aDkkYDUkYDUQWDEkYDUsZDkkYDT8VDD0UCz0UCzgSCjgSCjYSCjYSCjUSCjYS - CjUSCjEQCTAQCTEQCTAQCS4QCS4QCSsOCCwPCCsOCCsOCCcNByQMBycNBycNByQMByQMByILBiILBiIL - BiALBiILBiUNByUNByALBh0KBhsJBRoJBRgJBRgJBdbV1P////////////////////////////////// - /////2xpaBEGAxEGAxEGAxEGAxEGAxEGAw8FAxEGAxEGAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwF - AwwFAwwFA9XU1P///////////////////////////////////////6SkpAMBAQIBAQMBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9pLK9pLK9pOLtlJKdhHJtlIJ9pLK9pOLtpOLtpNLdpN - LdpOLtpNLdpNLdpOLtpPMN1aPN1eQd5iRt5iRt9mS99mS95iRt5iRt9mS+BrUOBqT+BqT+BsUuFvVeFu - VOFwV+JyWeJ0W+J1XON4YOR9ZuN7Y+N4YON5YeR/aOWDbeaHcueLduiRfemWg+uejOqZh+mXhemTgOiP - e+mVgumVgumWg+mXheqaiOyjk+yklO2ome+wou+zpfC5rPK/s/K/s/LAtfLDuPLBtvLDuPG8sPG6ru6u - n+yjk+mWg+iPe+aHceR9ZuJ0W+BsUt5iRtxYOtxXOdpOLtZGJsxDJMVBI74/Irg9IbE7IK05H6U3Hp40 - HJszHJIwGosuGYsuGYAqF3cnFXImFW8lFGsjE2sjE2gjE2QhEmEhEloeEFoeEFoeEFcdEFUcD1AbD1Ab - D00aDkcYDUkYDUYYDUYYDUQWDEIWDEIWDEQWDEEWDDwUCzoUCzgSCjUSCjMRCTMRCTMRCTAQCTAQCTAQ - CS4QCS4QCSwPCCwPCCwPCCsOCCkOCCkOCCQMByUNBycNByQMByQMByQMByALBiILBh8LBh0KBiALBh8L - Bh8LBh8LBiALBh8LBh0KBh0KBtbV1P///////////////////////////////////////2xpaBEGAxEG - AxEGAxEGAw8FAxEGAw8FAw8FAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgoDAgoDAtXU1P// - /////////////////////////////////////6SkpAIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9pOLtlJKdlIJ9lJKdlIJ9ZGJtpNLdpLK9pLK9pLK9pLK9pOLtpLK9lJKdpO - Lt1aPN1eQd5gRN9lSt9kSN5gRN5iRt5iRt9kSOBqT99kSOBoTd9lSuFuVOBrUOFvVeFuVOJyWeJ0W+J0 - W+J1XON3XuN4YOR9ZuN5YeR9ZuaFb+aHcuaHceaHcuiPe+iSfumVguiSfueNeeeNeeeNeeeMd+iRfemT - gOmWg+uejOufjuyjk+ymlu6un++zpfC3qvG8sPG6rvG8sPG9sfG6rvC3qu+0p+yjk+qciueNeeaHcuR/ - aOJyWeBrUOBrUN1bPtpPMNpLK9ZGJs5EJcVBI8A/Ir0/Irg9IbE7IKg3HqM2HaI2HZcyG5EwGosuGYMs - GHkoFncnFXImFWsjE2kjE2gjE2EhEl4fEVwfEVcdEFcdEFcdEFMcD1IbD1IbD00aDkkYDUYYDUYYDUYY - DUIWDEQWDEEWDD8VDD0UCz0UC0EWDEEWDEEWDDgSCjMRCTEQCTEQCTEQCTAQCSwPCCwPCC4QCSwPCCkO - CCkOCCsOCCcNBycNByUNByUNBycNByQMByILBiQMByILBiALBh8LBh0KBh8LBhsJBRsJBRsJBRsJBRgJ - BRgJBRgJBdbV1P///////////////////////////////////////2xpaBEGAxEGAxEGAw8FAw8FAxEG - Aw8FAw4FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwoDAgoDAtXU1P////////////////// - /////////////////////6SkpAIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9pLK9pNLdlJKdlIJ9lJKdlJKdpOLtpOLtpLK9lJKdlJKdpOLtpOLtpPMNtSM9xYOt5gQ95gRN5g - RN5iRt9kSN5gRN5gRN5iRuBqT99mS+BoTeBrUOBrUOBrUOBsUuJ0W+FuVOFwV+JyWeJ0W+FvVeN4YON5 - YeR8ZeN5YeR/aOR8ZeWCbOaHcuaHcueNeeeNeeeNeeaJdOaHcueLduaJdOeMd+eMd+iRfemVgumWg+qa - iOyklOymlu6uoO+ypO+zpe+0p/C3qvC2qe+0p++wou6uoOuejOmVguaHceR8ZeJ1XOBqT95iRt1bPtpP - MNlJKdFFJc9EJcdCJMJAI7s9Ibg9IbM7IKg3HqI2HZw0HJw0HJQxG40uGYouGX4qF3ooFncnFW8lFGsj - E2gjE2YhEmEhEl8fEV4fEVgdEFcdEFUcD1UcD1UcD04aDkkYDUYYDUcYDUcYDUYYDUEWDEEWDD8VDD0U - CzwUCzgSCjgSCjgSCjwUCzoUCzoUCzgSCjMRCTEQCTEQCTEQCSwPCCsOCCsOCCwPCCkOCCkOCCUNByQM - ByQMByQMByUNByQMByILBiILBiILBiALBiALBh0KBh0KBhsJBRsJBRoJBRgJBRYHBBgJBRgJBdbV1P// - /////////////////////////////////////2xpaBEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8FAw8F - Aw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwwFA9XU1P////////////////////////////////// - /////6WkpAMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9hHJtpNLdhH - JthHJtpLK9lJKdpLK9pNLdlJKdpOLtpLK9lJKdpPMNxVN91bPt5gQ95gRN5gRN9kSN9kSN5gQ95iRt9k - SN9mS99kSOBoTeFuVOBrUOBqT+BrUOFuVOFvVeFuVOFvVeFuVOJyWeN4YOR9ZuWBauR/aON7Y+N5YeN5 - YeR8ZeaHcuaHceaHcueLduaHcuaHceWDbeaHcuWDbeWDbeaFb+eLduiPe+iPe+mVguqaiOyike2omO6s - ne6un++ypO+ypO+ypO+wou2ome2omeqaiOeLduaFb+N5YeBrUN9kSN1bPttRMtpNLdhHJs5EJclCJMJA - I70/Irk9IbQ7IKg3Hqg3Hpw0HJkyG5IwGosuGYgtGIAqF3wpFncnFXAlFHImFWsjE2sjE2QhEmEhEl4f - EV4fEVgdEFMcD1UcD1UcD04aDlAbD0kYDUkYDUQWDEQWDEEWDEEWDD8VDDwUCz0UCzgSCjgSCjYSCjUS - CjgSCjYSCjUSCjYSCjYSCjYSCjMRCTMRCTEQCTAQCS4QCSsOCCsOCCkOCCkOCCUNByQMByQMByILBiIL - BiILBiILBh8LBh8LBh8LBh0KBhsJBRsJBRsJBRoJBRoJBRoJBRgJBRgJBdbV1P////////////////// - /////////////////////21qaRQHBBMHBBEGAxEGAxEGAxEGAxEGAw8FAw8FAw4FAw4FAw4FAw4FAwwF - AwwFAwwFAwoDAgoDAgwFAwoDAtXU1P///////////////////////////////////////6SkpAMBAQMB - AQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9lIJ9lIJ9pOLtlIJ9hHJtlIJ9pO - LtpNLdpOLtpLK9pLK9pNLdtSM91bPt1aPN1eQd5gRN1eQd1bPt5gQ99lSt9kSN5gQ95iRt9kSOBrUN9m - S+BsUuBrUOBrUOFuVOBsUuBsUuBrUOFwV+N3XueLdueNeeeMd+aJdOWDbeN4YON4YON5YeWBauaFb+WC - bOaHceaFb+WDbeWBauWBauR9ZuR/aOR/aOaFb+aHcuaHcueMd+mTgOqZh+yike2omO2ome6uoO6sne2q - m+2qm+yklOyklOmVguaFb+N7Y+FuVN5gQ91eQdxVN9pPMNhHJtNGJsxDJMJAI7s9IbQ7IK86H686H6c3 - HqA1HZszHJEwGo8wGoouGX4qF3koFnkoFnUnFW8lFGsjE2YhEmYhEmMhEl8fEV4fEVcdEFcdEFMcD1cd - EFIbD04aDk4aDkkYDUYYDUYYDUcYDUQWDEEWDDoUCz8VDDwUCzgSCjgSCjUSCjUSCjUSCjUSCjMRCTMR - CTMRCTEQCTMRCTYSCjUSCjEQCSwPCCsOCCsOCCcNByUNByQMByQMByQMByILBiILBiALBiILBiALBh8L - Bh0KBhsJBRoJBRoJBRsJBRoJBRoJBRoJBRgJBRYHBNbV1P////////////////////////////////// - /////2xpaBEGAxEGAxEGAxEGAxEGAxQHBBQHBBEGAw8FAw4FAw8FAw8FAw4FAwwFAwwFAwwFAwwFAwwF - AwoDAgoDAv///////////////////////////////////////////4iHhwIBAQIBAQMBAQMBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9lIJ9VGJthHJtVGJtZGJtlJKdpNLdpOLtlIJ9pNLdpL - K9pOLttUNdxYOtxYOtxYOt1aPN1bPt1eQd9kSN9kSN5gROBrUOBrUOBoTeBrUOBoTeFuVOBsUt9mS+Bq - T+BrUOBsUuJyWeN7Y+eMd+mVguqaiOiSfuiRfeeMd+R9ZuR8ZeJ0W+N5YeN4YOR/aOR/aOR9ZuN5YeR8 - ZeN5YeN3XuN3XuN7Y+R/aOWBauWDbeaJdOeMd+mWg+qciuyjk+ymlu2omO2qm+ymluyjk+ufjuuejOiP - e+R9ZuJyWd9mS91aPNxYOttRMtZGJtFFJcxDJMdCJL0/Irg9IbE7IK86H6g3Hp40HJszHJIwGpIwGogt - GIAqF34qF3koFnkoFnQnFW8lFGkjE2YhEmMhEmEhEmMhElwfEVcdEFcdEFMcD1IbD04aDksZDk0aDkcY - DUIWDEQWDEEWDEIWDDwUCz8VDDwUCzoUCzgSCjYSCjMRCTgSCjMRCTMRCTMRCTMRCTEQCS4QCS4QCS4Q - CTAQCS4QCTAQCTAQCS4QCSsOCCcNByUNByUNByQMByILBiILBiALBiILBiALBiALBh0KBhsJBRoJBRsJ - BRoJBRoJBRsJBRsJBRgJBRgJBdbV1P///////////////////////////////////////2xpaBEGAxEG - AxEGAxEGAw8FAxEGAxEGAw8FAxEGAxEGAxEGAxEGAxEGAw8FAw4FAwwFAwwFAwwFAwoDAgoDAv////// - /////////////////////////////////////2hnZwIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9lJKdhHJtZGJtZGJtpLK9lIJ9pOLtpOLtpOLtlJKdpNLdtUNdxYOtxVN91a - PNxXOd1aPNxYOt5gQ95iRt9kSOBrUOBqT99lSuBrUOBrUOFuVOBsUuBrUOBqT+FuVOFwV+J0W+N5YeaH - cuqZh+2omO2omOqaiOiSfueLduR/aOJ0W+J1XOFwV+JyWeN3XuN4YON7Y+N5YeN5YeJ1XOJ1XOJ0W+N3 - XuN7Y+R8ZeR8ZeWDbeaHcuiRfeqZh+ugj+yikeyklOyklOyklOugj+mXhemXheaHceJ0W+BqT95gQ9tS - M9pOLtpNLc9EJcxDJMpDJMJAI7k9IbQ7IK86H6g3Hp40HJszHJYyG5EwGpIwGoUsGIAqF3koFnkoFnIm - FW8lFG8lFGsjE2QhEl8fEV4fEV8fEVoeEFoeEFgdEFMcD1IbD04aDksZDk0aDkYYDUYYDUQWDD8VDD8V - DEEWDDwUCzwUCzgSCjYSCjUSCjUSCjUSCjMRCTMRCTEQCTAQCTAQCS4QCSsOCCwPCCsOCCsOCCsOCCkO - CCsOCDAQCS4QCSsOCCkOCCcNByUNByILBiILBiALBh8LBh8LBh0KBhsJBRoJBRoJBRoJBRsJBRsJBRoJ - BRYHBBgJBdbV1P///////////////////////////////////////2xpaBEGAxEGAxEGAxEGAw8FAw8F - Aw4FAw8FAw8FAw8FAw4FAw8FAw4FAw8FAxEGAxEGAxEGAw8FAwwFA2tpaP////////////////////// - /////////////////////1ZVVQIBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9lIJ9lIJ9ZGJthHJtpLK9lJKdpNLdlIJ9hHJtpLK9pOLtxXOdxXOdxYOt1aPN1cP9xXOd1cP95g - Q95gQ+BrUOBoTd9kSN9kSN9lSuBqT+BrUOBqT+BrUOFuVOFvVeJ0W+R9ZuaHcumWg+2omPG6rvC3quue - jOiRfeiPe+WCbON3XuN3XuBrUOBrUOFvVeFwV+J0W+N7Y+JyWeFwV+BsUuFuVOFvVeJ0W+JyWeN3XuN5 - YeaFb+eNeeiSfuqaiOufjuugj+yjk+yjk+qaiOmTgOiPe+WDbeBsUt5iRt1bPtpPMNZGJtNGJsxDJMVB - I74/Irg9Ibg9Ia05H6g3HqM2HZ40HJszHJEwGoouGYYsGIAqF3koFncnFXQnFW0kFG0kFG8lFGgjE2Mh - El8fEV8fEVwfEVgdEFgdEFMcD1McD1McD1AbD00aDkkYDUQWDEIWDEEWDEEWDD0UCz8VDDwUCzoUCzgS - CjUSCjYSCjMRCTUSCjYSCjMRCTEQCTAQCS4QCS4QCS4QCS4QCSwPCCwPCCsOCCkOCCkOCCcNBycNBykO - CCkOCCsOCCsOCCUNByQMByILBiALBiALBh8LBh0KBhsJBRsJBRoJBRsJBRoJBRoJBRYHBBgJBdbV1P// - /////////////////////////////////////2xpaBEGAxEGAxEGAw8FAw8FAxEGAw8FAw8FAw4FAw4F - AwwFAwwFAwwFAwwFAwwFAwwFAwwFAwwFAwwFA5iXl/////////////////////////////////////// - /////wIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lIJ9VGJthH - JtpLK9pLK9pOLtpLK9pNLdpOLtpLK9tUNd1bPtxXOdxXOd1bPtxXOdxYOt1bPt5iRt5gRN9lSt9kSN9m - S95iRt5gQ95iRuBrUOBrUOBoTeBrUOFwV+N5YeaHcuiRfeqciu+wovC2qe6uoOufjuiSfuaHcuR8ZeJ1 - XOFuVOBrUOBrUOBrUOFuVOFuVOFwV+FwV+BsUuBrUN9mS99mS+BrUOBrUOFwV+N3XuR8ZeaFb+eMd+mW - g+qciuufjuufjuufjumXheeMd+eMd+R/aOBoTd1aPNxVN9lIJ9NGJsxDJMVBI70/Ir0/IrM7ILM7IKI2 - HZszHJw0HJszHJEwGo8wGogtGIAqF3ooFnkoFncnFXImFW8lFG0kFGsjE2YhEmMhElwfEVgdEFoeEFcd - EFcdEFUcD1IbD1McD04aDk0aDkcYDUQWDEIWDEQWDEEWDEEWDDwUCzwUCzgSCjgSCjoUCzUSCjUSCjMR - CTMRCTMRCTEQCS4QCTAQCTAQCSwPCCwPCCkOCCkOCCkOCCUNBycNByUNByQMByUNByQMByUNBycNBycN - BycNByQMByILBiILBiALBhsJBRoJBRsJBRoJBRoJBRgJBRYHBBYHBBYHBNXV1P////////////////// - /////////////////////2xpaBEGAxEGAxEGAxEGAw8FAxEGAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwwF - AwwFAwwFAwoDAgoDAgoDAsnJyf///////////////////////////////////////+Df3wUCAQMBAQIB - AQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9pLK9hHJtZGJtlJKdlJKdpLK9lJ - KdhHJtlJKdpOLtxVN9xVN9xVN9xVN9tUNdxXOd1bPt5gQ95gRN5gRN1eQd1eQd5gQ95gQ95gQ95iRt9l - St9mS+BqT+BrUOFuVOWBaueLduiSfuufjuymlu2omeyikemWg+eNeeaHceFwV+FvVeFuVOFvVd9lSt9l - SuBoTd9mS+BqT+BqT+BqT+BrUN9kSN5iRuBqT+BoTeBrUOFuVOJ0W+R/aOaHcumTgOmTgOqciuqZh+qZ - h+mVguaJdOWDbeN5Yd5gRNtRMtpNLdVGJs9EJcdCJL0/Irs9IbQ7ILE7IKo4HqU3HqA1HZszHI8wGoou - GYMsGIErF3koFnooFncnFXImFW8lFG0kFGkjE2gjE2YhEl8fEV8fEVoeEFUcD1cdEFcdEFIbD1AbD1Ab - D1AbD04aDkcYDUcYDUYYDT8VDEEWDD0UCzgSCjgSCjgSCjwUCzYSCjUSCjMRCTEQCTMRCTMRCS4QCTAQ - CSwPCCwPCCsOCCsOCCkOCCkOCCcNBykOCCcNByUNByUNByUNByQMByILBiILBiILBiALBiALBiUNByUN - ByALBhsJBRsJBRsJBRsJBRsJBRgJBRYHBBQHBBQHBNXV1P////////////////////////////////// - /////2xpaBEGAxEGAw8FAw8FAw4FAw8FAw8FAw4FAw8FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoD - AiwoJ////////////////////////////////////////////769vQoDAgcDAgMBAQMBAQMBAQMBAQMB - AQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9lJKdpNLdlIJ9pOLtpLK9lJKdpLK9hHJtpOLttRMtxV - N9tRMtxYOtxXOdxYOt1aPN1aPN1eQd5iRt5iRt1bPt5gQ95gQ91eQd1cP91eQd9kSN9kSN9mS+BoTeFu - VOR8ZeeLduiSfumWg+qaiOmXheiPe+aFb+WBauN3XuBrUOFvVeFuVOJ1XOBqT95iRt5gRN5iRt9lSt5i - Rt9mS99lSt5gQ95iRt1eQd5gRN5iRt9mS+FvVeN4YOR/aOeLduiRfemXhemXheqZh+iSfuaHceR8ZeN3 - Xt1eQdpLK9VGJs9EJcJAI70/Irk9IbM7IK05H6o4HqI2HaM2HZcyG5YyG48wGoMsGIUsGIAqF3cnFXko - FnAlFG8lFG0kFGsjE2YhEmMhEl8fEV8fEV8fEVgdEFUcD1UcD1McD1McD1AbD00aDkkYDUsZDkcYDUQW - DEEWDEEWDD0UCz0UCzoUCzYSCjUSCjYSCjMRCTMRCTMRCTMRCTMRCS4QCS4QCS4QCSwPCC4QCSwPCCsO - CCkOCCkOCCcNBykOCCcNBycNByUNByUNByILBiALBiALBiALBiALBh8LBh8LBiALBiALBiALBiALBh8L - Bh0KBhsJBRoJBRgJBRYHBBQHBNXV1P///////////////////////////////////////2xpaBEGAxEG - Aw8FAw8FAw8FAw8FAw8FAw4FAw8FAw8FAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAoqIiP////////// - /////////////////////////////////4mIhwUCAQMBAQUCAQUCAQMBAQcDAgcDAgUCAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9VGJtZGJtpLK9pOLtpOLtpNLdpNLdhHJtlIJ9pOLtxYOtxVN9tUNdxVN91b - Pt1bPt1eQd1cP91bPt5gQ91bPt5gQ95gRN1bPt1cP91eQd5gRN9kSOBoTd9kSN9mS+N3XuWCbOaJdOiP - e+eMd+aFb+N4YOFvVeBsUuBrUN9lSuBrUOBrUOBrUN9lSt1eQd1eQd1eQd1bPt1eQd5gRN1cP9xYOt1a - PN1bPt1bPt1eQd9lSuBqT+JyWeR/aOaHcueLduiSfumTgOmVgumTgOWBauJ1XOFwV9xXOdlIJ8xDJMVB - I7k9Ibg9Ia05H6o4Hqc3HqM2HZ40HJszHJEwGosuGYMsGIAqF4AqF3koFnooFncnFXImFW0kFG0kFGQh - EmEhEl8fEVwfEV4fEVcdEFcdEFMcD1AbD04aDk4aDk4aDksZDksZDkkYDUQWDEYYDUIWDEEWDEEWDD8V - DDgSCjYSCjYSCjUSCjUSCjMRCTMRCTMRCTMRCTAQCTAQCS4QCSwPCCwPCCsOCCsOCCsOCCkOCCcNBycN - ByUNByUNByQMByILBiILBiILBiILBiALBiALBh0KBh8LBh0KBh0KBhsJBR0KBh8LBh8LBhsJBRsJBRoJ - BRgJBRQHBNXV1P///////////////////////////////////////2xpaA8FAw8FAxEGAw8FAw8FAw8F - Aw4FAwwFAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgkDAurq6v////////////////////////// - /////////////////ygmJgMBAQIBAQMBAQMBAQMBAQMBAQMBAQMBAQUCAQUCAQUCAQMBAQMBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9hHJtlIJ9lJKdpLK9lJKdpLK9lIJ9pNLdpLK9pPMNxVN9xXOdxXOdxYOtxYOt5gQ91eQd5gQ95g - Q91eQd5gQ91eQd1eQd1eQd1cP91aPN1cP95gRN9kSN9lSuBqT+BsUuJ1XOR9ZuN7Y+R8ZeN4YOBsUuFv - VeBrUOBqT+BqT+BrUN9mS99mS99kSN1aPN1aPN1bPtxYOt1eQd1bPtxVN9xVN9tSM9tRMttUNdtUNd1e - Qd9lSuBsUuN3XuWDbeaHcuiPe+iRfeiSfueMd+R8ZeFvVeBrUNpNLdFFJcRBI8A/Irk9Ia86H6o4HqA1 - HZ40HJkyG5IwGo8wGo8wGoUsGHooFn4qF3ooFnQnFXcnFXUnFW0kFGkjE2gjE2MhEmEhEl8fEVoeEFgd - EFgdEFUcD1AbD04aDlIbD04aDk0aDk0aDkkYDUcYDUEWDEIWDD8VDD0UC0EWDD0UCzYSCjYSCjMRCTYS - CjMRCTMRCTMRCTEQCTEQCTMRCTAQCSwPCCwPCCwPCCwPCCsOCCsOCCkOCCcNBycNByUNByQMByQMByIL - BiILBiQMByILBiALBiALBh0KBh8LBh0KBh0KBhsJBRsJBRoJBRoJBRgJBRsJBRoJBR8LBhoJBdbV1P// - /////////////////////////////////////2xpaBEGAxEGAxEGAw8FAw8FAw8FAw4FAw4FAw4FAw4F - Aw4FAwwFAwwFAwwFAwoDAgoDAgoDApiXlv///////////////////////////////////////////9TU - 1AMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9ZGJtZGJtpN - LdpNLdpNLdlIJ9lJKdhHJtpNLdtRMtxXOdtRMtxXOdxXOdxVN95gQ91bPt1eQd1bPt1cP91cP91bPt1b - Pt1cP95gRN1bPt1bPt5gQ95gRN9kSN9mS99mS+BrUOFuVOFwV+FwV+JyWeFvVeBrUN9kSN9kSOBoTd5i - Rt5iRt9kSN5gRNxYOt1aPNxXOdxVN9tRMttSM9tSM9pOLtpOLtlIJ9pNLdpOLtxVN91cP99mS+JyWeR8 - ZeaHceeLdueNeeiPe+eLduN3Xt9mS95iRtpNLcVBI70/Irg9Ia05H6g3HqI2HZw0HJcyG5IwGpEwGoou - GYouGYMsGHooFnooFnkoFnooFnQnFXImFWsjE2YhEmYhEl8fEVwfEVwfEVcdEFcdEFUcD1UcD1AbD1Ib - D1IbD04aDkcYDUcYDUYYDUYYDUEWDEEWDEEWDD8VDD0UCz0UCzwUCzYSCjYSCjUSCjUSCjMRCTEQCTEQ - CTEQCTAQCS4QCSwPCCwPCCwPCCsOCCkOCCcNBycNBycNByUNByUNByQMByQMByQMByILBiILBiALBiAL - Bh8LBh8LBh0KBhsJBRsJBRoJBRoJBRgJBRoJBRgJBRgJBRYHBBgJBRgJBdbV1P////////////////// - /////////////////////2xpaBEGAw8FAw8FAw8FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwF - AwoDAgoDAllXVvX19f///////////////////////////////////////////4iHhwUCAQMBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9ZGJtlIJ9pOLtpNLdlJKdhHJtlI - J9lJKdlIJ9tRMtxXOdxVN9xYOttUNd1eQd1aPN1eQd1cP91bPt1bPt1bPt1aPN1cP9xYOt1bPt1bPt1b - Pt1cP91bPt5gQ91eQd5gROBoTeBrUOBrUOFuVOFvVeBrUOBqT99mS95gQ95gRN5gRN5iRt9kSN5gQ9tS - M9tUNdtRMtpOLtpOLttSM9pNLdlIJ9lIJ9hHJthHJtZGJttRMttRMt1aPN9mS+JyWeWBauaJdOaJdOaH - cuaHceBsUt5gRN1eQdZGJrs9Iaw5H6o4Hqg3HqA1HZszHJcyG5IwGo8wGogtGIAqF4ErF4AqF3koFnko - FnQnFXQnFW0kFGkjE2sjE2kjE2MhElwfEVwfEVwfEVcdEFcdEFcdEFAbD1IbD04aDk4aDkkYDU0aDkYY - DUQWDD8VDEEWDD8VDD8VDDoUCzoUCzYSCjwUCzgSCjUSCjYSCjYSCjMRCTEQCTEQCS4QCS4QCSwPCCwP - CCsOCCwPCCsOCCsOCCcNByUNByUNByQMBycNByQMByQMByILBiILBiALBh8LBiALBh0KBh8LBhsJBRsJ - BRsJBRsJBRsJBRoJBRoJBRYHBBgJBRgJBRYHBBQHBNXV1P////////////////////////////////// - /////2xpaA8FAw4FAxEGAw8FAw8FAw4FAw4FAw4FAwwFAw4FAwwFAwwFAwwFAwwFAwwFAywoJ+Df3/// - //////////////////////////////////////////X19SgmJgMBAQIBAQIBAQIBAQMBAQIBAQIBAQIB - AQIBAQAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9lIJ9VGJtlIJ9lJKdZGJtpOLtVGJthHJtlJKdtRMttS - M9xYOtxVN9xXOdxXOd1bPtxYOtxYOtxXOdxYOtxXOdtUNdxVN9xVN91cP91bPt1cP91eQd1aPN5gQ95i - Rt1cP95gRN9mS+BqT+BoTeBqT99lSt5iRt5gQ95iRt5iRt1eQd1eQd1eQd1cP9pPMNpOLtpOLtpLK9pL - K9pNLdlIJ9ZGJtNGJtVGJtNGJtVGJtNGJtpNLdtUNd5gROFuVON7Y+aFb+aHcuaHceR/aN9mS9tUNdpP - MMlCJLM7IKg3HqI2HaA1HZcyG5YyG5EwGpIwGoouGYUsGIUsGH4qF3ooFnkoFnImFXQnFW0kFGsjE2Yh - EmMhEmMhEl8fEVgdEFoeEFgdEFgdEFcdEFUcD1IbD1IbD0sZDk0aDksZDkkYDUcYDUQWDEQWDD8VDD0U - Cz8VDDgSCjgSCjoUCzYSCjgSCjYSCjYSCjMRCTAQCTEQCS4QCS4QCSwPCCwPCCwPCCsOCCsOCCkOCCkO - CCcNByUNByQMByQMBycNByQMByILBiALBiILBiILBiALBiALBh8LBh0KBh0KBhsJBR0KBhsJBRsJBRoJ - BRoJBRYHBBQHBBQHBBYHBBQHBNXV1P///////////////////////////////////////2xpaA8FAw8F - Aw8FAw8FAw8FAw4FAw4FAw4FAwwFAw4FAwwFAwwFAwwFAwwFA0VBQODf3/////////////////////// - /////////////////////////6SkpAMBAQMBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQAA - AAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9lJKdhHJtZGJtpOLtlIJ9pLK9pLK9pLK9hHJttUNdtRMtxXOdtUNdtSM91c - P91aPNxYOtxVN91aPN1aPNtSM91aPNxVN91aPN1aPNxYOt1aPN1aPNxXOd1bPt1eQd5gQ95iRt5gRN9m - S95iRt9lSt5gQ91eQd1eQd1bPt1cP91eQd1cP91cP9tSM9lJKdpPMNlIJ9pLK89EJdVGJthHJtVGJs9E - JdNGJsxDJMpDJM9EJdVGJtpNLdxYOuBrUON3XuWCbOaHceWCbON5Yd5gRNpNLdlJKcRBI6w5H6M2HaA1 - HZszHJYyG40uGYsuGYsuGYMsGIErF4ErF3ooFncnFXQnFXAlFGsjE2sjE2sjE2EhEmMhEl8fEV4fEV8f - EVoeEFgdEFcdEFcdEFIbD1McD04aDk0aDksZDkcYDUYYDUQWDEYYDT8VDDwUCzoUCzwUCzgSCjoUCzoU - CzYSCjUSCjMRCTMRCTMRCTMRCTAQCS4QCS4QCS4QCSsOCCsOCCsOCCsOCCwPCCkOCCcNByUNByUNByUN - ByQMByQMByILBiILBiILBiALBiALBiALBh8LBh0KBh0KBh0KBhsJBRoJBRgJBRgJBRgJBRQHBBQHBBQH - BBQHBBQHBNXV1P///////////////////////////////////////21qaREGAxEGAw8FAw4FAw8FAw4F - Aw4FAw4FAwwFAwwFAwwFAwwFAwwFA4qIiPX19f////////////////////////////////////////// - //////X19SgmJgMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJtlIJ9lIJ9lJKdlJKdpNLdlJKdlJKdhHJttRMttUNdtSM9tUNdtUNd1aPN1aPNxXOdxVN9xY - OttRMtxVN91bPtxXOd1aPNxYOtxXOdxYOtxXOdxVN9xYOt1eQd1eQd5gQ95iRt9kSN5gQ95iRt1cP91a - PN1bPt1cP91aPNxXOdxYOtxVN9pOLtlIJ9lIJ9NGJtZGJtVGJtZGJtFFJcxDJMVBI8xDJMVBI8pDJM5E - Jc5EJdlIJ9pOLt5iRuFwV+R8ZeWBauR/aOJyWdxYOtlJKdNGJr0/IqI2HZw0HJcyG5QxG48wGosuGYgt - GIYsGIAqF3wpFnooFnUnFXcnFXImFXQnFXAlFGgjE2QhEl4fEWEhEmEhElwfEVcdEFUcD1UcD1McD1Mc - D1AbD04aDk0aDkkYDU4aDkkYDUIWDEIWDEQWDDwUCzwUCzoUCzwUCzgSCjYSCjgSCjMRCTMRCTMRCTUS - CjUSCjMRCTEQCTEQCS4QCS4QCSwPCC4QCSsOCCsOCCkOCCcNByUNBycNBycNByUNByQMByQMByILBiAL - BiILBiALBiALBh8LBh0KBh0KBh0KBh0KBhsJBRoJBRgJBRgJBRYHBBQHBBYHBBYHBBYHBBQHBNXV1P// - /////////////////////////////////////2xpaBEGAxQHBBEGAw8FAw8FAw8FAw4FAw4FAw4FAw4F - Ay4qKIqJiODg3////////////////////////////////////////////////////////5eWlgIBAQIB - AQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lIJ9NGJtpN - LdpNLdlJKdlJKdhHJthHJtlIJ9tSM9tSM9xVN9xVN9tRMtxVN91eQd1aPNxVN9xVN9xVN9tSM9xVN91b - PttUNdxXOdxVN9xXOdxVN9xVN9xXOdxYOt1aPN1bPt1aPN1bPt1bPt1cP91eQd1bPtxXOdxYOttUNdpO - LttRMttRMtpLK9NGJtFFJdFFJc9EJcxDJMpDJNNGJspDJMJAI8A/Ir0/IsRBI8VBI8VBI85EJdpNLd1a - POFuVON3XuR9ZuN5YeBsUtpPMMxDJMdCJLs9IZszHJYyG5EwGo8wGosuGYgtGIErF34qF4AqF3koFncn - FXcnFXImFW0kFG8lFGsjE2EhEmEhElwfEV4fEVoeEFoeEFcdEFMcD1UcD1McD1AbD00aDk0aDk4aDkkY - DUkYDUcYDUIWDEQWDD8VDDoUCzoUCzoUCzoUCzYSCjYSCjMRCTMRCTEQCTMRCTMRCTMRCTAQCTEQCTEQ - CS4QCSwPCCsOCCwPCCkOCCkOCCkOCCcNByUNByUNByUNByQMByQMByILBiILBiALBiALBiALBiALBh8L - BhsJBRsJBRsJBRsJBRsJBRoJBRgJBRgJBRgJBRYHBBQHBBQHBBYHBBYHBNXV1P////////////////// - /////////////////////769vaalpKalpKalpKalpKalpKalpKalpKalpMrJyeDg3/////////////// - /////////////////////////////////////////////////+Df3wMBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9lJKdVGJthHJtpOLtpOLtpLK9NG - JtpLK9ZGJtpOLttSM9tUNdtUNdtUNd1aPN1aPN1bPt1bPtxXOdxVN9tSM9tRMttUNdtUNdxVN9xVN9tS - M9tUNdtUNdxXOd1aPNxYOtxXOd1aPN1bPt1bPt1aPN1aPNxXOdxVN9tUNdtSM9tUNdtSM9pNLdlIJ9NG - JtNGJsxDJNFFJcxDJMlCJMpDJMdCJL0/Ir0/Irs9Ib0/Ir4/Irg9IcRBI85EJdtUNeBoTeJyWeN4YOJ0 - W99mS9lJKb4/Ir0/IrM7IJszHI8wGoouGYsuGYgtGIMsGIAqF3wpFnUnFXcnFXImFW8lFG0kFGgjE2gj - E2gjE2MhElwfEVwfEVgdEF8fEVgdEFUcD1IbD1IbD1AbD04aDksZDkkYDUcYDUsZDkYYDUYYDUIWDD8V - DD8VDD8VDDgSCjoUCzgSCjYSCjUSCjMRCTMRCTMRCTUSCjEQCTAQCS4QCTAQCS4QCTAQCS4QCTAQCSsO - CCkOCCkOCCcNByUNByQMByUNByQMByILBiILBiALBiALBiILBiILBiALBh0KBh0KBhsJBR0KBhsJBRsJ - BRsJBRoJBRgJBRYHBBgJBRYHBBYHBBQHBBMHBBMHBNXV1P////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////X19VZVVQMBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQAAAAIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9VGJtlIJ9VGJtlJKdlIJ9lIJ9pLK9lJKdlIJ9pLK9tR - MtxXOdxVN9pOLttSM91aPN1bPtxXOdxYOtxXOdxXOdtUNdtSM9tSM9xVN9tUNdtUNdtSM9tSM9xVN9xY - OtxYOt1bPtxVN91bPt1aPNxYOttSM9tRMtpNLdpOLtpOLtpPMNlJKdlIJ9ZGJsxDJM5EJclCJMpDJMdC - JMVBI8RBI7s9Ibs9Ib0/Ir0/Irg9Ibg9IbY8Ibk9IcdCJNlJKd5gQ+BsUuJ1XOJyWd5gQ85EJbE7IK86 - H7M7IJcyG48wGogtGIMsGIYsGIErF3koFnkoFnQnFXcnFXImFW8lFGgjE20kFGYhEmYhEmEhEl4fEV8f - EVoeEFcdEFcdEFMcD1AbD1AbD04aDksZDk0aDk0aDkcYDUkYDUQWDEYYDUQWDEEWDD8VDDgSCjgSCjgS - CjoUCzYSCjYSCjMRCTMRCTMRCTEQCTAQCTAQCTAQCTAQCSwPCC4QCSwPCC4QCSsOCCkOCCkOCCcNBycN - ByQMByILBiILBiQMByILBh8LBiALBiALBiALBh8LBh0KBh0KBhsJBR0KBhoJBRoJBRoJBRgJBRgJBRgJ - BRYHBBQHBBYHBBQHBBMHBBMHBNXV1P////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////4iHhwMBAQMBAQIBAQIBAQMBAQMBAQMBAQMBAQIBAQIBAQAAAAIBAQAAAAAAAAIBAQIB - AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9hHJtlJKdNGJtpLK9pLK9lIJ9lJKdlJKdhHJtZGJtpOLttUNdtSM9pPMNtU - Nd1aPNxYOttUNdxVN9tRMttRMtpOLttUNdxVN9tUNdtRMttSM9tUNdtSM9xVN9tUNdxVN9tRMttUNdxX - OdxXOdxXOdpOLtpNLdpOLtpOLtpNLdlJKdpNLdhHJs9EJcdCJMJAI8dCJMJAI74/IsA/Ir0/Irk9Ibk9 - IbY8IbE7ILM7ILE7IK86H7E7ILs9Ic5EJdxXOeBrUOJ0W+FvVdxVN8A/IqU3HqA1Ha05H5YyG4YsGH4q - F4AqF4ErF3wpFnkoFnUnFXQnFW8lFGkjE2kjE2sjE2QhEmQhEmMhEmEhElwfEVwfEVcdEFMcD1UcD1Ib - D04aDk4aDksZDksZDkkYDUkYDUYYDUYYDUEWDEYYDUEWDDwUCzoUCzgSCjYSCjYSCjUSCjUSCjUSCjMR - CTEQCTAQCTAQCTAQCTAQCS4QCSwPCCwPCCwPCCsOCCsOCCwPCCkOCCkOCCcNBycNByQMByILBiILBiQM - ByILBh8LBh8LBh8LBiALBh8LBh0KBhsJBR0KBh0KBhsJBRoJBRoJBRoJBRoJBRYHBBYHBBQHBBQHBBMH - BBQHBBQHBNXV1P////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////5eWlgMB - AQMBAQMBAQMBAQIBAQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9ZGJs5EJdZGJtlIJ9pNLdpLK9ZGJthHJthHJthHJtpOLtpPMNtSM9tUNdtUNdxXOdxYOttUNdtU - NdtRMttRMttSM9xVN9pPMNpOLttRMtpPMNxVN9tSM9tSM9xVN9xVN9pOLtxXOdxXOdtUNdtUNdpLK9lJ - KdpNLdhHJtlJKdlJKdlIJ9FFJcRBI8JAI8JAI8A/Ir0/Ir0/Ir0/Irs9IbY8IbY8IbM7IK86H6o4HqU3 - Hq05H6o4Hq05H74/ItpNLd9lSuJ0W+BqT9pNLbY8IZw0HJ40HKw5H5YyG4AqF34qF3wpFncnFXkoFnIm - FXAlFG0kFG0kFGsjE2YhEmgjE2EhEmEhEl8fEV4fEVgdEFgdEFcdEFIbD1AbD1McD00aDksZDk0aDk0a - DkcYDUkYDUcYDUIWDEEWDEIWDD0UCzoUCzwUCzgSCjgSCjUSCjUSCjMRCTMRCTUSCjMRCTMRCTAQCSwP - CCwPCCwPCCwPCC4QCSsOCCkOCCkOCCsOCCkOCCsOCCcNByUNByQMByQMByILBiILBiALBh8LBh8LBh8L - Bh8LBh8LBh0KBhsJBRsJBR0KBhsJBRoJBRoJBRgJBRgJBRYHBBYHBBYHBBYHBBMHBBMHBBMHBNXV1P// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////7GwsAMBAQMBAQMBAQMBAQMBAQMB - AQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lJKdZGJtlI - J9ZGJthHJtlJKdlJKdhHJtZGJthHJtlIJ9tRMttSM9tSM9tRMttRMtxVN9xYOttUNdtUNdtUNdpPMNtS - M9tRMtpOLtpOLtpPMNtRMttSM9tSM9tRMttRMttUNdtSM9tUNdtRMtlIJ9pLK9pLK9lJKdVGJthHJtZG - JtNGJspDJMA/Ir4/Ir0/Ir4/Irs9Ib0/IrY8IbQ7ILE7ILE7IKw5H6w5H6U3Hqg3HqM2HaI2HaM2HbE7 - IMdCJNxXOeBoTd1bPslCJJ40HI0uGZEwGp40HJEwGn4qF3koFnkoFncnFXUnFXAlFHAlFGkjE2sjE2kj - E2YhEmYhEl4fEV4fEVwfEVwfEVwfEVcdEFcdEFAbD1AbD1AbD00aDk0aDkcYDUcYDUkYDUYYDUIWDEQW - DD0UC0EWDDoUCzgSCjoUCzgSCjgSCjYSCjYSCjMRCTUSCjEQCTMRCTMRCTAQCSwPCDAQCS4QCSsOCCsO - CCsOCCkOCCsOCCkOCCUNBycNBycNBycNByQMByQMByILBiILBiILBiALBh8LBh8LBh0KBh0KBh0KBhsJ - BRsJBRoJBRgJBRgJBRgJBRYHBBYHBBYHBBYHBBYHBBYHBBYHBBMHBBMHBNXV1P////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////4mIhwMBAQMBAQIBAQIBAQMBAQIBAQMBAQMBAQIBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9lJKdlIJ9VGJtlIJ9hHJthHJtVG - JtpNLdNGJthHJtlIJ9pLK9pPMNtRMttUNdpPMNtRMttUNdxYOtxVN9tUNdtRMtpOLtpOLtpPMNtRMtpO - LtpOLtpNLdpPMNpOLttRMttRMttUNdtSM9lJKdpNLdpLK9lIJ9pLK9ZGJtZGJtFFJcxDJMVBI8A/Ir0/ - Ir0/Irs9Ibs9Ibk9IbQ7ILE7IKw5H6w5H6g3HqM2Hac3HqM2HaA1HZszHJkyG6A1HbQ7INZGJtxVN9lJ - KbE7IJQxG4gtGIouGZw0HI8wGnooFnwpFnQnFXUnFXQnFXImFW0kFGkjE2YhEmQhEmEhEl8fEVgdEFoe - EFgdEFcdEFcdEFcdEFAbD04aDk4aDk0aDlAbD0kYDUkYDUsZDkQWDEYYDUEWDEEWDEEWDD0UCzoUCzgS - CjoUCzgSCjgSCjMRCTMRCTMRCTEQCTMRCTEQCTEQCSwPCC4QCTEQCSsOCCsOCCsOCCwPCCcNBycNBykO - CCUNByQMByUNByUNByUNByUNByQMByALBiALBiALBiALBh0KBh8LBh0KBhsJBRsJBRsJBRoJBRgJBRoJ - BRgJBRgJBRgJBRYHBBYHBBQHBBQHBBYHBBMHBBMHBNXV1P////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////+rq6lhWVQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQAA - AAIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9hHJtlIJ9NGJtZGJtVGJtlIJ9lIJ9lJKdNGJtlIJ9VG - JtVGJtpOLtpPMNpOLttRMtpOLtpOLttRMtpOLttUNdtSM9tSM9pOLtpOLtpPMNpOLtpLK9pOLtpPMNpN - LdpOLtpOLtpNLdpOLtZGJtNGJs9EJdNGJtNGJtNGJtNGJs9EJcVBI74/IsA/Irg9IbY8IbQ7ILg9IbM7 - ILM7IKw5H6g3HqU3HqM2HaU3HqA1HaA1HZkyG5cyG5kyG5cyG5szHLs9IcxDJL4/IpszHIUsGH4qF4Er - F5YyG40uGXkoFnQnFXImFXImFXImFWsjE2YhEmgjE2YhEmQhElwfEVoeEFoeEFoeEFcdEFUcD1cdEFcd - EFIbD1IbD00aDk0aDkcYDUYYDUkYDUYYDUQWDEQWDEEWDEIWDD8VDDoUCzwUCzgSCjgSCjMRCTUSCjEQ - CTUSCjMRCS4QCS4QCTAQCS4QCSwPCC4QCS4QCSwPCC4QCSsOCCkOCCcNBycNBykOCCcNByUNByUNByQM - ByQMByUNByUNByALBiALBh8LBiALBh8LBh8LBhsJBRsJBRsJBRoJBRoJBRgJBRoJBRgJBRYHBBYHBBYH - BBgJBRYHBBQHBBQHBBMHBBMHBNXU1P////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////6WkpCwoJwUC - AQMBAQMBAQMBAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9hHJtFFJdVGJtFFJdhHJtlIJ9hHJtVGJtFFJdhHJtlJKdhHJtlJKdpPMNpO - LtpOLtpNLdpOLtpOLtpPMNpPMNtRMtpPMNpOLttSM9pOLtpOLtpLK9lJKdpLK9pOLtpNLdpNLdlJKdpL - K9VGJthHJtFFJdFFJdFFJc5EJclCJMRBI7k9Ibg9Ibg9IbQ7ILQ7ILE7ILE7ILE7IKo4Hqo4HqU3HqA1 - HaA1HZw0HJkyG5szHJYyG5QxG40uGY0uGYouGZcyG6c3HpszHI0uGX4qF3ooFnooFpEwGo0uGXQnFW0k - FHAlFGsjE2sjE2kjE2YhEmMhEmMhElwfEVoeEFgdEFcdEFcdEFMcD1McD1McD1IbD1UcD00aDk0aDkkY - DUkYDUcYDUQWDEQWDEIWDEIWDEEWDD0UCz0UCzYSCjYSCjUSCjYSCjMRCTMRCTAQCTMRCTMRCTAQCS4Q - CTAQCTAQCS4QCSwPCCwPCCsOCCsOCCsOCCkOCCkOCCcNBycNBykOCCcNByUNByILBiILBiILBiQMByIL - BiALBh8LBh0KBh0KBh8LBhsJBRsJBRsJBRoJBRgJBRgJBRYHBBYHBBYHBBYHBBQHBBQHBBQHBBMHBBMH - BBMHBBMHBNXU1P////////////////////////////////////////////////////////////////// - //////////////////////////////////////////X19bGwsERBQAkDAgcDAgUCAQMBAQUCAQMBAQIB - AQIBAQMBAQMBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9hHJtlIJ9FFJdNGJtNGJtFFJdhHJtZGJthHJtZGJtNGJtFFJc5EJdVGJtpOLtpNLdpNLdpLK9pN - LdpNLdpOLtpOLtpPMNxVN9tRMtpPMNlJKdpOLtpLK9pLK9lJKdlJKdNGJtVGJtNGJtNGJsxDJNNGJspD - JMpDJMdCJMJAI7s9Ibg9IbM7ILM7IK86H605H6w5H605H6o4HqA1HaA1HZszHJ40HJszHJkyG5QxG5cy - G5EwGo8wGosuGYouGYMsGIgtGIUsGIouGYMsGHwpFnkoFnooFosuGYouGXUnFW8lFG8lFGgjE2YhEmYh - EmQhEl8fEVwfEVwfEVgdEFgdEFcdEFUcD1IbD1McD1AbD04aDlIbD00aDkkYDUkYDUkYDUcYDUQWDD0U - C0EWDD8VDD8VDD8VDDoUCzYSCjYSCjMRCTUSCjMRCTUSCjEQCTEQCTAQCTAQCTEQCTEQCTAQCS4QCTAQ - CSsOCCwPCCkOCCkOCCcNBycNByUNByUNByUNByUNByQMByILBiALBiILBiILBh8LBh8LBiALBh8LBh8L - Bh0KBh0KBh0KBhsJBRoJBRYHBBgJBRYHBBgJBRQHBBQHBBQHBBMHBBMHBBMHBBMHBBMHBBEGA9XU1P// - //////////////////////////////////////////////////////////////////////////////// - /////////////+rq6r69vXt5eAkDAgkDAgkDAgkDAgcDAgMBAQUCAQUCAQMBAQIBAQMBAQMBAQMBAQIB - AQIBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9lIJ9FFJdNG - JtNGJtZGJtNGJtZGJtZGJtZGJthHJtFFJdNGJs9EJdhHJtlIJ9pNLdlJKdpNLdpLK9hHJtpLK9lJKdlI - J9pNLdlJKdlIJ9pNLdZGJtZGJtVGJtZGJtVGJs5EJcxDJM5EJcVBI8pDJMxDJMlCJMRBI8JAI70/IrY8 - IbE7IK05H7Q7IKo4Hqw5H6o4Hq05H6I2HaA1HZszHJw0HJszHJkyG48wGpQxG40uGYsuGYgtGIMsGIUs - GIUsGIAqF4ErF4UsGH4qF3koFnUnFXQnFYMsGIgtGHQnFW0kFGkjE2QhEmYhEmQhEl4fEV4fEVcdEFcd - EFcdEFUcD1cdEFcdEFUcD1AbD04aDkkYDU0aDk0aDkcYDUcYDUkYDUQWDEQWDEIWDD8VDDwUCz0UCz0U - CzoUCzUSCjUSCjYSCjYSCjUSCjMRCTEQCTEQCTAQCS4QCS4QCSwPCDAQCSwPCCwPCCsOCCkOCCkOCCcN - BykOCCcNByUNByQMByQMByQMByQMByQMByILBiILBiALBh8LBh0KBh8LBiALBiALBh0KBhsJBRsJBRoJ - BRoJBRgJBRgJBRYHBBYHBBYHBBYHBBYHBBQHBBMHBBMHBBMHBBMHBBEGA4yJiaalpKalpKalpKalpKal - pKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKalpKWkpKalpHt5eGpoaCwoJwoD - AgoDAgoDAgoDAgkDAgkDAgcDAgUCAQMBAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIB - AQIBAQIBAQIBAQIBAQAAAAIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9VGJtZGJtVGJtNGJtFFJdFFJdVG - JtZGJtVGJtZGJs9EJc9EJcxDJM5EJc5EJdFFJdNGJthHJtpNLdlIJ9pLK9lIJ9lIJ9ZGJtVGJs9EJc9E - JdNGJtVGJtFFJcxDJMdCJM5EJclCJMdCJMdCJMdCJMRBI8JAI7s9IbY8IbM7IK05H6o4Hqw5H605H6w5 - H6o4Hqc3HqA1HZszHJszHJszHJkyG5kyG5QxG5EwGo0uGYYsGIYsGIUsGIErF4ErF3wpFnkoFn4qF3oo - FnkoFnQnFXAlFHAlFIAqF4gtGHQnFWQhEmQhEmMhEmEhEmMhEl8fEVwfEVcdEFoeEFUcD1IbD1McD1Ab - D04aDlAbD0sZDkkYDUkYDU4aDk0aDkYYDUQWDEIWDD8VDEEWDDwUCz8VDEEWDDoUCzUSCjUSCjYSCjMR - CTMRCTMRCTAQCTEQCTAQCTAQCTAQCS4QCSwPCCwPCCwPCCsOCCsOCCwPCCkOCCcNBycNByUNBycNByUN - ByQMByQMByILBiILBiILBiALBiALBiALBh8LBh8LBh8LBh8LBhsJBRsJBRsJBRoJBRoJBRgJBRgJBRgJ - BRYHBBYHBBYHBBYHBBYHBBQHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw4FAw8F - Aw4FAw4FAw4FAw4FAw4FAwwFAw4FAwwFAwwFAwwFAwoDAgoDAgoDAgwFAwoDAgoDAgoDAgoDAgoDAgkD - AgkDAgUCAQUCAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQMBAQIBAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9hHJtZGJtNGJtFFJdFFJc9EJdFFJdNGJtNGJtVGJs9E - Jc9EJc9EJc5EJcxDJMdCJNFFJdNGJthHJtlIJ9lJKdFFJdVGJtNGJtFFJdFFJc5EJc9EJdFFJdFFJclC - JMlCJMdCJMdCJMxDJMVBI74/Ir4/IrM7IK86H7Q7IK05H686H6g3Hqg3Hqg3Hqc3Hqg3HqA1HZszHJky - G5kyG5IwGpIwGpEwGo0uGYgtGIYsGIYsGIUsGIAqF4MsGIErF4AqF3koFnkoFnkoFncnFW8lFG0kFG0k - FHooFoUsGHAlFGEhEmMhElwfEVwfEV4fEVoeEFoeEFUcD1McD1UcD1McD1AbD1AbD04aDksZDkkYDUcY - DUYYDUYYDU0aDkQWDEEWDEQWDD8VDDoUCz8VDDwUCzwUCzYSCjUSCjUSCjMRCTMRCTUSCjMRCTMRCTEQ - CTAQCS4QCSwPCCwPCC4QCS4QCSwPCCwPCCcNBycNBykOCCcNBycNByUNByUNByILBiQMByQMByILBiAL - BiILBh8LBh8LBh8LBh0KBhsJBR0KBhsJBRsJBRsJBRoJBRoJBRoJBRYHBBgJBRgJBRgJBRYHBBQHBBMH - BBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw4FAw8FAw8FAw4FAw4FAw4FAw4F - Aw4FAwwFAw4FAwwFAw4FAw4FAwwFAwoDAgoDAgoDAgoDAgoDAgkDAgoDAgkDAgkDAgkDAgUCAQMBAQMB - AQMBAQUCAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAIBAQAAAAAAAAAAAAAA - AAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////9lIJ9NGJtNGJs9EJc9EJdFFJcpDJM9EJc5EJc9EJc5EJcxDJM9EJcVBI8lC - JMpDJMVBI8pDJNVGJtFFJdVGJtFFJc5EJcxDJM5EJcxDJM5EJcpDJMxDJMdCJMdCJMJAI8VBI8lCJL0/ - Ir0/IrY8IbQ7IK86H686H6w5H6g3HqM2Hag3HqI2HaM2HaA1HaA1HZkyG5szHJQxG5QxG5EwGo0uGY0u - GYgtGIUsGIUsGIUsGIAqF4AqF3ooFnooFncnFXcnFXcnFXcnFXQnFW0kFGsjE2sjE3QnFYMsGHImFWEh - El8fEVwfEVwfEVoeEFoeEFcdEFcdEFUcD1IbD04aDlAbD00aDksZDkkYDUcYDUcYDUcYDUYYDUIWDEIW - DEEWDDwUCz0UCzwUCz0UCzwUCzgSCjYSCjMRCTMRCTMRCTEQCTEQCTEQCTAQCTAQCS4QCTAQCS4QCS4Q - CSsOCCwPCCkOCCsOCCsOCCcNBykOCCcNBycNByUNByUNByILBiILBiILBiILBiALBiILBh8LBh8LBh8L - Bh0KBhsJBRsJBRsJBRsJBRsJBRoJBRsJBRsJBRgJBRgJBRYHBBYHBBMHBBMHBBMHBBEGAxEGAxEGAxMH - BBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw4FAw4FAw4FAw4FAw4FAw4FAw4FAwwFAwwFAw4FAw4F - Aw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgkDAgkDAgkDAgkDAgcDAgMBAQMBAQMBAQMBAQMBAQMBAQMB - AQMBAQMBAQMBAQIBAQMBAQIBAQIBAQAAAAIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9VGJtFFJdVGJtFFJdZGJtFFJc5EJcpDJM5EJcpDJM5EJcpDJMpDJMRBI8dCJMVBI8RBI8RBI8dC - JMlCJMxDJMlCJMpDJMxDJNFFJcpDJMxDJMlCJMVBI8VBI8dCJL0/IsJAI74/IrY8Ia05H605H6I2Ha05 - H6c3HqU3HqU3Hp40HJ40HKA1HaA1HZszHJQxG5YyG4ouGY8wGo0uGZEwGo0uGYYsGIMsGIAqF34qF4Aq - F3wpFnooFncnFXcnFXQnFXImFXAlFHQnFW8lFGsjE2sjE2kjE3ImFX4qF2sjE2QhElwfEVgdEFcdEFgd - EFcdEFMcD1IbD1AbD1IbD00aDk0aDk0aDkYYDUcYDUYYDUYYDUYYDUIWDEIWDEYYDUEWDD8VDDwUCz0U - CzwUCzoUCzYSCjYSCjUSCjMRCTMRCTMRCTAQCS4QCTAQCTAQCS4QCSwPCCwPCCsOCCkOCCkOCCsOCCsO - CCsOCCUNBykOCCcNByUNByQMByQMByILBiQMByILBiILBiALBh8LBh0KBh8LBh8LBh0KBhsJBRsJBRsJ - BRoJBRoJBRoJBRoJBRoJBRYHBBQHBBQHBBYHBBQHBBMHBBQHBBMHBBEGAxEGAxMHBBEGAxEGAxEGAxEG - AxEGAw8FAxEGAw8FAw8FAxEGAw8FAw4FAw8FAw8FAw4FAwwFAw4FAwwFAwwFAw4FAw4FAwwFAwwFAwoD - AgoDAgoDAgoDAgkDAgkDAgoDAgoDAgcDAgUCAQMBAQMBAQUCAQMBAQMBAQMBAQIBAQIBAQMBAQMBAQIB - AQMBAQIBAQAAAAAAAAIBAQIBAQAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////9VGJtZGJtNG - Js9EJc9EJcxDJM9EJc9EJc9EJcxDJMpDJMpDJMpDJMVBI8dCJMdCJMA/IsRBI8A/IsA/Ir0/Ir0/Ir4/ - IsRBI8JAI74/Ir0/Ir0/Ir0/Ir0/Irs9Ibs9IbY8Ia86H6w5H6g3Hqg3Hqc3Hqc3Hqc3Hp40HKA1HZ40 - HJkyG5kyG5QxG5YyG5EwGo0uGYsuGYYsGIErF4UsGIYsGIUsGIErF4AqF3wpFnooFnooFnkoFncnFXAl - FG8lFHQnFW8lFHAlFG8lFGQhEmgjE2QhEmgjE3UnFW0kFF4fEVgdEFoeEFUcD1cdEFgdEFUcD1McD04a - Dk0aDk0aDkYYDUsZDksZDkkYDUIWDEEWDEIWDEEWDEEWDEEWDEEWDDwUCz0UCzwUCzoUCzUSCjMRCTUS - CjMRCTMRCTEQCTAQCS4QCTAQCS4QCS4QCSwPCCwPCCsOCCwPCCsOCCkOCCcNBycNBycNBycNBykOCCcN - ByUNByQMByQMByILBiILBiILBiILBiALBh8LBh0KBh8LBh0KBhsJBRsJBRsJBRsJBRoJBRoJBRgJBRgJ - BRoJBRgJBRgJBRQHBBYHBBQHBBQHBBQHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8F - Aw8FAxEGAw8FAw4FAw4FAw4FAw4FAwwFAw4FAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgkDAgoDAgkD - AgkDAgkDAgoDAgcDAgMBAQMBAQUCAQMBAQMBAQMBAQMBAQIBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIB - AQIBAQIBAQAAAAAAAAAAAAIBAQIBAQAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9NGJtZGJtNGJsxDJNNGJslCJMxD - JMpDJMpDJMlCJMlCJMdCJMJAI8dCJMVBI8VBI8JAI8RBI8RBI70/IsA/Irk9Ib0/Irk9Ibg9IbY8IbQ7 - ILM7ILY8IbE7IK86H686H6w5H6w5H6U3Hqc3HqU3HqA1HaA1HaI2HZw0HKA1HZcyG5szHJEwGo0uGY8w - GosuGYsuGYYsGIErF4UsGIMsGIAqF3ooFnooFnooFnkoFnUnFXImFXAlFHUnFXAlFGkjE28lFGYhEmsj - E2QhEmEhEmMhEmEhEmYhEnQnFW0kFFgdEFgdEFUcD1McD1McD1UcD1McD00aDk0aDlAbD0kYDUIWDEcY - DUIWDEIWDD8VDD8VDD0UCz0UCz0UCz0UCz0UCz8VDD0UCzgSCjgSCjUSCjUSCjYSCjMRCTEQCTEQCTAQ - CS4QCSwPCC4QCSwPCDAQCS4QCSsOCCsOCCsOCCcNBycNBycNBycNByUNByUNByUNByQMByQMByILBiIL - BiALBiALBh8LBh8LBh8LBh0KBh0KBhsJBRsJBRoJBRoJBRoJBRoJBRgJBRgJBRYHBBgJBRYHBBYHBBQH - BBYHBBQHBBMHBBMHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw4FAw4F - Aw4FAw4FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwwFAwwFAwoDAgoDAgoDAgoDAgoDAgoDAgkDAgcDAgUC - AQMBAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQMBAQMBAQIBAQIBAQIBAQAAAAAAAAIBAQAAAAIBAQAA - AAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9FFJc9EJdFFJcxDJM9EJcpDJMpDJMpDJMlCJMJAI8JA - I74/IsdCJMJAI8JAI8VBI8A/Ir4/Ir0/IsJAI70/Irg9Ibs9IbY8IbM7ILE7IK86H686H686H6o4Hqw5 - H605H6g3Hqg3Hq05H6U3HqI2HaI2HZ40HKA1HZkyG5kyG5cyG5IwGo8wGosuGYgtGIgtGIouGYUsGIMs - GIMsGIMsGH4qF3koFnkoFnkoFncnFXUnFXUnFW8lFGkjE2sjE20kFGsjE2YhEmkjE2MhEl4fEV8fEV8f - EWMhEm0kFGsjE1wfEVMcD1McD1IbD1IbD1AbD1AbD1IbD0kYDUQWDEcYDUYYDUIWDEQWDEQWDD8VDEEW - DDoUCzwUCz0UCzwUCzwUCz8VDDgSCjgSCjMRCTMRCTMRCTMRCTEQCTEQCTAQCTAQCS4QCS4QCSwPCCwP - CC4QCSsOCCkOCCsOCCcNBykOCCcNBysOCCcNByUNByUNByQMByILBiQMByILBiALBiILBiALBh8LBh8L - BiALBh0KBhsJBRoJBRoJBRoJBRsJBRsJBRgJBRYHBBQHBBYHBBgJBRYHBBMHBBQHBBQHBBQHBBQHBBMH - BBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAxEGAw8FAw4FAw4FAwwFAw4FAw4FAw4F - Aw4FAw4FAw4FAwwFAwoDAgoDAgwFAwoDAgoDAgoDAgoDAgoDAgoDAgcDAgUCAQUCAQUCAQMBAQMBAQMB - AQMBAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAIBAQAAAAIB - AQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////89EJdNGJtFFJcxDJNFFJclCJMpDJMpDJMdCJMRBI8A/IsA/IsJAI8JAI8RB - I8VBI8A/IsJAI7k9Ibs9Ibk9Ibk9IbQ7ILg9IbM7ILE7ILE7ILE7IK86H6o4HqU3Hqw5H6o4Hqc3HqA1 - HZ40HKM2HaA1HaI2HZszHJkyG5szHI8wGosuGYgtGIgtGIMsGIgtGIErF4ErF34qF4AqF4AqF3koFncn - FXQnFXUnFXQnFXAlFG0kFGkjE2sjE2gjE2sjE2EhEmMhEmQhEmMhElwfEVgdEFgdEFwfEWsjE2gjE1Uc - D1McD1AbD00aDk0aDk0aDk0aDksZDksZDkcYDUQWDEcYDUYYDUEWDD8VDDwUCzwUCzwUCzoUCzoUCzwU - CzoUCzoUCzgSCjYSCjMRCTEQCTEQCTEQCTEQCTEQCS4QCTAQCS4QCSwPCCsOCCsOCCkOCCkOCCsOCCcN - BycNBycNBycNBycNBycNByUNByUNByQMByILBiILBiALBiALBiALBiALBh8LBh0KBh0KBhsJBR0KBhsJ - BRsJBRsJBRoJBRoJBRoJBRYHBBQHBBYHBBYHBBYHBBQHBBQHBBMHBBMHBBQHBBQHBBMHBBMHBBMHBBEG - AxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw4FAw4FAw4FAwwFAw4FAw4FAwwFAwwFAw4FAwwFAwwF - AwoDAgoDAgoDAgoDAgoDAgoDAgkDAgkDAgkDAgcDAgUCAQMBAQMBAQIBAQMBAQMBAQMBAQMBAQMBAQIB - AQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////9FFJcxDJM5EJcdCJMxDJMpDJMlCJMpDJMdCJMA/IsA/Ir0/Ir0/IsA/Ir0/Ir4/Ir4/Irs9Ibg9 - IbQ7IL0/IrQ7ILQ7ILM7IK86H6w5H6w5H605H6w5H6c3Hqc3Hqc3HqU3Hqc3HqM2HZszHJw0HJszHJcy - G5IwGpYyG48wGosuGYsuGYUsGIMsGIErF3wpFn4qF34qF3ooFnwpFncnFXQnFXQnFXQnFW0kFGkjE2Yh - EmgjE2kjE2YhEmEhEmEhEl8fEWMhEmEhEl4fEVcdEFcdEFcdEFoeEGgjE2YhElcdEFIbD0sZDkkYDUkY - DUsZDkkYDUkYDUYYDUYYDUQWDEQWDD8VDEEWDD8VDD8VDD0UCz0UCz8VDDoUCzUSCjoUCzUSCjgSCjUS - CjMRCTEQCTAQCS4QCTAQCS4QCTAQCS4QCS4QCSwPCCwPCCsOCCcNBycNBykOCCkOCCcNBykOCCUNByUN - ByQMByUNByQMByILBiALBiILBiILBiILBiALBiALBh8LBh0KBhsJBRsJBRsJBRsJBRsJBRsJBRoJBRgJ - BRgJBRgJBRgJBRQHBBYHBBYHBBQHBBYHBBMHBBEGAxMHBBMHBBQHBBEGAxEGAxEGAxEGAxEGAxEGAxEG - Aw8FAw8FAw8FAw8FAw8FAw4FAw4FAw4FAwwFAwwFAw4FAw4FAwwFAwwFAwwFAwoDAgwFAwoDAgoDAgoD - AgoDAgkDAgkDAgkDAgkDAgUCAQMBAQUCAQMBAQMBAQUCAQMBAQMBAQMBAQIBAQIBAQMBAQIBAQMBAQIB - AQIBAQIBAQIBAQAAAAAAAAAAAAIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8xDJMxDJM5E - JcdCJMpDJMlCJMlCJMJAI8JAI74/IsJAI70/Ir0/Ir4/Irs9Ibk9Ibk9Ibg9Ib0/IrY8IbY8IbQ7ILQ7 - ILM7ILE7IK86H6o4HqU3Hqg3HqU3HqI2HaI2HaI2HZw0HJw0HJ40HJszHJQxG5EwGo0uGYYsGIouGYou - GYMsGIYsGIErF3koFnwpFnwpFnkoFnQnFXcnFXkoFnUnFW8lFHAlFGsjE2kjE2gjE2MhEmEhEmEhEl4f - EV8fEVwfEV4fEV4fEVcdEFUcD1UcD1UcD1UcD2EhEl8fEVcdEFAbD0kYDUsZDk0aDkcYDU0aDkkYDUEW - DD8VDEEWDD8VDEEWDD0UCzoUCz0UCzwUCzoUCzoUCzYSCjUSCjgSCjUSCjYSCjUSCjMRCTEQCS4QCS4Q - CTAQCS4QCSwPCCwPCCwPCCwPCCsOCCkOCCcNBykOCCkOCCkOCCcNBycNByQMByQMByQMByQMByQMByIL - BiALBiALBiALBiALBh8LBiALBh8LBh0KBhsJBRsJBRoJBRoJBRoJBRsJBRgJBRgJBRYHBBgJBRgJBRYH - BBQHBBQHBBQHBBQHBBQHBBEGAxEGAxEGAxEGAxMHBBMHBBEGAxEGAxEGAxEGAxEGAw8FAw8FAw8FAw8F - Aw4FAw4FAw8FAw4FAw4FAw4FAw4FAwwFAwwFAwwFAwoDAgoDAgwFAwoDAgkDAgkDAgkDAgkDAgoDAgkD - AgcDAgMBAQMBAQMBAQMBAQMBAQMBAQUCAQMBAQMBAQIBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQAA - AAAAAAIBAQIBAQAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////9NGJs9EJc5EJcxDJMlCJMxDJMlC - JMdCJMVBI74/Ir4/Ir0/Ir0/Ir0/IrY8IbY8IbY8Ia05H7M7ILQ7ILQ7ILM7IKw5H686H6w5H6U3Hqg3 - Hqg3HqM2HZw0HKI2HZ40HKA1HZw0HJszHJ40HJQxG48wGosuGYgtGIgtGIErF34qF4UsGHwpFn4qF3cn - FXooFnkoFnQnFXImFXcnFXQnFXAlFG8lFGkjE2kjE2sjE2gjE2MhEmMhEl4fEV4fEV4fEV4fEVwfEVoe - EFoeEFMcD1UcD1AbD1AbD14fEV8fEVcdEE4aDkcYDUkYDUkYDUkYDUcYDUkYDUQWDEIWDEEWDD8VDD8V - DDgSCjgSCjwUCzoUCzwUCzYSCjUSCjUSCjMRCTEQCTMRCTYSCjMRCTAQCS4QCTAQCTEQCSwPCCwPCCwP - CCsOCCsOCC4QCSsOCCkOCCkOCCkOCCcNByUNBycNByQMByQMByQMByQMByILBiILBiILBiALBh8LBh8L - Bh8LBh0KBh0KBhsJBRsJBRsJBRsJBRsJBRsJBRgJBRgJBRYHBBYHBBQHBBYHBBQHBBQHBBMHBBMHBBMH - BBQHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw4FAw8FAw4FAw8FAw8FAw4FAw4FAw4F - AwwFAw4FAw4FAwwFAwwFAwwFAwoDAgoDAgwFAwoDAgoDAgoDAgoDAgkDAgkDAgcDAgUCAQMBAQMBAQUC - AQMBAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQMBAQIBAQMBAQIBAQIBAQIBAQIBAQIBAQAAAAIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////9NGJsxDJMxDJM9EJcpDJMxDJMJAI8dCJMJAI8A/IsA/ - Ir0/Irg9IbY8IbY8IbE7IK86H7M7IK86H686H6g3Hqc3Hqg3Hqc3Hqc3HqI2HaI2HZ40HJ40HJ40HJsz - HJszHJYyG5YyG5QxG4suGY0uGYsuGY0uGYMsGIAqF4MsGH4qF3wpFn4qF3ooFncnFXImFXkoFnUnFXUn - FXImFW0kFG0kFGgjE2YhEmQhEmQhEmEhEl8fEVwfEVwfEVwfEV4fEVoeEFgdEFcdEFMcD1AbD1IbD04a - Dk0aDlwfEV4fEVUcD0kYDUYYDUQWDEQWDEYYDUYYDUEWDEEWDD0UCz8VDDwUCzoUCzoUCzoUCzoUCzgS - CjgSCjYSCjUSCjMRCTUSCjEQCS4QCTMRCS4QCTEQCS4QCSsOCCwPCCwPCCwPCCwPCCkOCCsOCCkOCCsO - CCkOCCcNBykOCCQMByQMByUNByQMByILBiILBiQMBx8LBiALBiALBiALBiALBh8LBh8LBh0KBh0KBhoJ - BRoJBRoJBRsJBRoJBRoJBRYHBBYHBBgJBRgJBRQHBBQHBBYHBBYHBBQHBBMHBBMHBBMHBBMHBBMHBBEG - AxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw4FAw8FAw4FAw8FAw8FAw8FAw8FAw4FAwwFAw4FAwwFAwwF - AwwFAwwFAwoDAgkDAgkDAgkDAgkDAgoDAgkDAgoDAgcDAgUCAQMBAQUCAQMBAQMBAQUCAQUCAQMBAQMB - AQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQAAAAAAAAAAAAAAAAIBAQAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////8xDJMlCJM5EJcpDJMpDJMRBI8VBI8RBI8dCJMRBI70/Ir4/Irg9IbY8Ibg9 - IbY8IbY8IbM7IKc3Hqo4Hqg3HqI2HaU3HqM2HaM2HaI2HZw0HJszHJszHJcyG5YyG5kyG5QxG5EwGosu - GY0uGYsuGYUsGIUsGIErF3wpFnooFnooFnooFnwpFnUnFXUnFXQnFXQnFW0kFGsjE2sjE2kjE2gjE2Qh - EmMhEmMhEl8fEV4fEV4fEVwfEVoeEFgdEFUcD1cdEFUcD1cdEFAbD00aDk4aDksZDk4aDlcdEF4fEVMc - D0cYDUIWDEYYDUEWDEEWDEEWDEEWDEEWDD0UCz0UCzoUCzwUCzgSCjoUCzUSCjYSCjMRCTMRCTMRCTEQ - CTEQCTMRCTAQCTMRCTEQCSwPCC4QCSwPCCsOCCsOCCsOCCkOCCkOCCkOCCkOCCsOCCcNByUNByUNByUN - ByQMByILBiILBiQMByILBiILBiALBiALBh8LBh8LBh8LBh0KBh0KBh0KBhsJBRoJBRsJBRoJBRgJBRYH - BBgJBRgJBRgJBRYHBBYHBBQHBBMHBBQHBBQHBBMHBBEGAxMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEG - AxEGAxEGAxEGAxEGAw8FAw4FAw4FAw8FAw8FAw8FAw4FAwwFAwwFAwwFAwwFAwwFAwwFAwwFAwoDAgoD - AgoDAgwFAwoDAgoDAgkDAgkDAgcDAgUCAQUCAQUCAQUCAQMBAQUCAQUCAQMBAQMBAQMBAQMBAQIBAQIB - AQMBAQIBAQIBAQIBAQAAAAIBAQIBAQIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////8lCJM5EJcxDJMpDJMdCJMRBI8dCJL0/Irs9Ib4/Ir0/Irs9Ibg9IbQ7ILY8IbM7ILE7ILM7IKo4 - Hq05H6c3HqM2HZw0HJszHJ40HJ40HJszHJYyG5cyG5cyG5IwGo8wGo8wGosuGY0uGYouGYUsGIUsGIEr - F4AqF3wpFnkoFnkoFnUnFXcnFXcnFW8lFG8lFHAlFG8lFGkjE2gjE2QhEmMhEmMhEl8fEV4fEVwfEVwf - EVoeEFoeEFgdEFMcD1cdEE4aDk0aDlUcD04aDksZDksZDksZDksZDlIbD1UcD1McD0IWDEEWDEIWDEEW - DD8VDD0UC0EWDD8VDD0UCzwUCz0UCzYSCjYSCjMRCTUSCjgSCjMRCTMRCTAQCTMRCS4QCTEQCTAQCS4Q - CTAQCTEQCSwPCC4QCSsOCCkOCCcNBykOCCsOCCkOCCkOCCUNByUNByUNByQMByQMByQMByILBiILBiQM - ByILBiILBiALBiALBh8LBh0KBh0KBh0KBh0KBhsJBRsJBRoJBRsJBRoJBRgJBRYHBBgJBRoJBRoJBRYH - BBQHBBQHBBQHBBQHBBMHBBMHBBMHBBMHBBMHBBEGAxMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8F - Aw8FAw4FAw4FAw8FAw4FAw4FAw4FAwwFAwwFAwwFAwwFAwoDAgwFAwwFAwoDAgkDAgoDAgwFAwoDAgkD - AgkDAgkDAgUCAQUCAQUCAQUCAQUCAQMBAQMBAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQMBAQIBAQIBAQIB - AQIBAQIBAQIBAQAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////8pDJMpDJMpD - JMVBI8RBI8RBI70/Ir0/Ir0/Irs9Ibs9IbY8IbY8IbE7ILE7ILM7IK86H6w5H6o4HqU3HqM2HaI2HZsz - HJszHJw0HJkyG5kyG5YyG5QxG48wGo0uGYsuGYouGYMsGIgtGIYsGIMsGIAqF4AqF34qF3koFnQnFXQn - FXUnFXAlFHAlFG0kFHAlFGsjE2kjE2QhEmEhEmMhEl4fEV4fEVgdEFwfEVcdEFgdEFcdEFUcD1McD1cd - EFMcD04aDk0aDlAbD1AbD00aDkcYDUkYDUkYDUkYDVUcD1IbD0QWDD8VDD0UCz0UCzwUCzoUCz8VDD0U - CzoUCzoUCzUSCjYSCjYSCjMRCTMRCTMRCTMRCTMRCTEQCTMRCS4QCS4QCSwPCC4QCTAQCS4QCSsOCCsO - CCsOCCkOCCcNBysOCCkOCCcNBycNByUNByUNByUNByQMByQMByQMByILBiALBiILBiILBiALBh8LBh8L - Bh8LBh8LBhsJBRsJBRsJBRoJBRoJBRoJBRsJBRoJBRgJBRgJBRgJBRoJBRgJBRQHBBQHBBQHBBQHBBQH - BBQHBBMHBBMHBBMHBBEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAxEGAw8FAw8FAw4FAw4FAw8F - Aw4FAwwFAwwFAwwFAwwFAw4FAwwFAwwFAwoDAgoDAgoDAgkDAgkDAgoDAgoDAgkDAgcDAgUCAQMBAQMB - AQMBAQUCAQMBAQMBAQMBAQMBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIBAQIB - AQAAAAAAAAAAAAAAAAIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAP////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABMLAAATCwAAAAAAAAAA + AAD///////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ///////////////////28fr/18Tq/9jG6P/VxeT/1MXi/9LE3//QxNz/z8Pa/87D2P/LwtT/ysLR/8jB + zv/Hwcz/xsHL/8XAyf/FwMj/w8DH/8PAxv/Cv8T/wb/D/8LAw//CwMP/wcDC/8HAwv/BwMH/v7/A/76+ + vv+/v7//u7u7/+/v7////////////9nE7f9eC63/Xxak/1cSmf9SEJH/Tg+K/0kNgv9EDHj/Pglv/zEG + Wv8qBE3/IwJB/xwANv8WACz/EgAm/w8AIf8LABr/CAAV/wQADv8BAAn/AAAC/wAAAP8AAAD/AAAA/wAA + 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 + Bv8AAAD/vr6+////////////4Mn2/34g2P+JMtv/kUHd/5ZI3/+iXuP/tX7o/8OY7f/dxPT/1rny/6Rg + 5f94Icn/VBaO/zsSY/8yD1L/KAxC/yAKNv8cCS7/Gwsq/wAADP+rp67/29rc/wgADv8AAAD/q6ur/9ra + 2v8AAAD/AwME/wAAAP++vr7////////////fyPX/fh/X/4473P+WSN//oVvi/7F45/+lYuP/pGDj/7yL + 6v+yd+j/fibQ/2Ecof9IFnf/OBFd/y0OSv8kCzv/Hgky/xoIK/8ZCif/AAAK/6uorv/a2dv/AQAH/wAA + AP+zs7T/1NTU/wAAAP8EBAT/AAAA/76+vv///////////9/I9f9/Idf/jzzd/4883P+YTN//oFri/488 + 3f+DKtf/olzl/5RL2v9cFp3/Sxh7/zsSYv8xD1H/KAxB/yAKNf8cCC3/Fwcm/xcKJP8AAAj/qKWr/9va + 3P8BAAb/PTpA//////+IiIj/AAAA/wYGBv8AAAD/vr6+////////////38j1/30e1v+MNdz/izXc/4oz + 3P+IL9z/fCbN/2sdtP97LsT/biiw/0UTdP88E2T/Mg9S/yoNRf8iCjj/HQkw/xoIKv8VByP/Fgog/wAA + B/+gnaP///////Dw8P//////xsbG/xAPEf8AAAD/AwME/wAAAP++vr7////////////fyPX/eh3R/4Eq + 0v+CK9T/fijP/3Qjv/9mIKj/Wx2U/08Yg/9JF3j/PBRj/zIRUv8tD0j/JQ07/x8LM/8cCi3/GQkn/xUI + If8TCR3/CQER/z44Q/9jX2f/ZGBm/0lGTP8JCAr/AgIC/wQEBP8EBAT/AAAA/7+/v////////////93F + 8/9sD8P/bBm5/2MUrf9bEp//URCN/0UMev87CWr/Mgda/y0FUv8lA0X/HAA2/xcALv8TACb/DwAg/wwA + G/8IABb/BQAQ/wIAC/8AAAj/AAAC/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wICAv8AAAD/vr6+//// + ////////9vH8/9vE7//ax+z/18bn/9TF4//SxN7/z8Pa/83D1v/LwtP/ysLR/8jBz//Hwcz/xsHK/8XA + yf/EwMj/w8DH/8PAxf/CwMT/wb/D/8G/w//DwcT/xMPF/8PCxP/CwcP/v7/A/7+/v/++vr7/v7+//7u7 + u//v7+////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= \ No newline at end of file diff --git a/DiztinGUIsh/window/VisualizerForm.Designer.cs b/DiztinGUIsh/window/VisualizerForm.Designer.cs new file mode 100644 index 00000000..34a16095 --- /dev/null +++ b/DiztinGUIsh/window/VisualizerForm.Designer.cs @@ -0,0 +1,121 @@ +namespace DiztinGUIsh.window +{ + partial class VisualizerForm + { + /// + /// 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.panelTop = new System.Windows.Forms.Panel(); + this.panelLegend = new System.Windows.Forms.Panel(); + this.bankLegend1 = new DiztinGUIsh.window.usercontrols.BankLegend(); + this.panelBottom = new System.Windows.Forms.Panel(); + this.romFullVisualizer1 = new DiztinGUIsh.window.usercontrols.RomFullVisualizer(); + this.panelTop.SuspendLayout(); + this.panelLegend.SuspendLayout(); + this.panelBottom.SuspendLayout(); + this.SuspendLayout(); + // + // panelTop + // + this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelTop.Controls.Add(this.panelLegend); + this.panelTop.Dock = System.Windows.Forms.DockStyle.Top; + this.panelTop.Location = new System.Drawing.Point(0, 0); + this.panelTop.Name = "panelTop"; + this.panelTop.Size = new System.Drawing.Size(765, 154); + this.panelTop.TabIndex = 1; + // + // panelLegend + // + this.panelLegend.Controls.Add(this.bankLegend1); + this.panelLegend.Dock = System.Windows.Forms.DockStyle.Left; + this.panelLegend.Location = new System.Drawing.Point(0, 0); + this.panelLegend.Name = "panelLegend"; + this.panelLegend.Size = new System.Drawing.Size(567, 152); + this.panelLegend.TabIndex = 0; + // + // bankLegend1 + // + this.bankLegend1.AutoScroll = true; + this.bankLegend1.AutoSize = true; + this.bankLegend1.Dock = System.Windows.Forms.DockStyle.Fill; + this.bankLegend1.Location = new System.Drawing.Point(0, 0); + this.bankLegend1.Name = "bankLegend1"; + this.bankLegend1.Size = new System.Drawing.Size(567, 152); + this.bankLegend1.TabIndex = 0; + // + // panelBottom + // + this.panelBottom.AutoScroll = true; + this.panelBottom.AutoSize = true; + this.panelBottom.Controls.Add(this.romFullVisualizer1); + this.panelBottom.Dock = System.Windows.Forms.DockStyle.Fill; + this.panelBottom.Location = new System.Drawing.Point(0, 154); + this.panelBottom.Name = "panelBottom"; + this.panelBottom.Size = new System.Drawing.Size(765, 363); + this.panelBottom.TabIndex = 2; + // + // romFullVisualizer1 + // + this.romFullVisualizer1.Location = new System.Drawing.Point(3, 0); + this.romFullVisualizer1.MinimumSize = new System.Drawing.Size(100, 100); + this.romFullVisualizer1.Name = "romFullVisualizer1"; + this.romFullVisualizer1.Project = null; + this.romFullVisualizer1.Size = new System.Drawing.Size(666, 335); + this.romFullVisualizer1.TabIndex = 0; + // + // VisualizerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScroll = true; + this.ClientSize = new System.Drawing.Size(765, 517); + this.Controls.Add(this.panelBottom); + this.Controls.Add(this.panelTop); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; + this.Name = "VisualizerForm"; + this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "ROM Visualizer"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.VisualizerForm_FormClosing); + this.Load += new System.EventHandler(this.VisualizerForm_Load); + this.panelTop.ResumeLayout(false); + this.panelLegend.ResumeLayout(false); + this.panelLegend.PerformLayout(); + this.panelBottom.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Panel panelTop; + private System.Windows.Forms.Panel panelLegend; + private System.Windows.Forms.Panel panelBottom; + private usercontrols.BankLegend bankLegend1; + private usercontrols.RomFullVisualizer romFullVisualizer1; + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/VisualizerForm.cs b/DiztinGUIsh/window/VisualizerForm.cs new file mode 100644 index 00000000..11d18c66 --- /dev/null +++ b/DiztinGUIsh/window/VisualizerForm.cs @@ -0,0 +1,51 @@ +using System.Windows.Forms; +using Diz.Core.model; +using DiztinGUIsh.window.usercontrols; + +namespace DiztinGUIsh.window +{ + public partial class VisualizerForm : Form + { + private readonly MainWindow mainWindow; + private Project project; + + public Project Project + { + get => project; + set + { + project = value; + romFullVisualizer1.Project = project; + } + } + + public VisualizerForm(MainWindow window) + { + mainWindow = window; + InitializeComponent(); + } + + private void VisualizerForm_Load(object sender, System.EventArgs e) + { + mainWindow.ProjectController.ProjectChanged += ProjectController_ProjectChanged; + + // hack to make room for the scrollbar + // I wish docking dealt with this, or maybe I set it up wrong... + Width = romFullVisualizer1.Width + 40; + + romFullVisualizer1.Project = mainWindow.Project; + } + + private void ProjectController_ProjectChanged(object sender, controller.ProjectController.ProjectChangedEventArgs e) + { + this.Project = e.Project; + } + + private void VisualizerForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason != CloseReason.UserClosing) return; + e.Cancel = true; + Hide(); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/VisualizerForm.resx b/DiztinGUIsh/window/VisualizerForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DiztinGUIsh/window/VisualizerForm.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/DiztinGUIsh/window/dialog/About.Designer.cs b/DiztinGUIsh/window/dialog/About.Designer.cs index 0f46b476..383d3e34 100644 --- a/DiztinGUIsh/window/dialog/About.Designer.cs +++ b/DiztinGUIsh/window/dialog/About.Designer.cs @@ -27,7 +27,6 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(About)); this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); this.logoPictureBox = new System.Windows.Forms.PictureBox(); this.labelProductName = new System.Windows.Forms.Label(); @@ -43,7 +42,7 @@ private void InitializeComponent() // tableLayoutPanel // this.tableLayoutPanel.ColumnCount = 2; - this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 136F)); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 256F)); this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0); this.tableLayoutPanel.Controls.Add(this.labelProductName, 1, 1); @@ -61,33 +60,33 @@ private void InitializeComponent() this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 35F)); - this.tableLayoutPanel.Size = new System.Drawing.Size(338, 229); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); + this.tableLayoutPanel.Size = new System.Drawing.Size(673, 326); this.tableLayoutPanel.TabIndex = 0; // // logoPictureBox // this.logoPictureBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.logoPictureBox.Image = ((System.Drawing.Image)(resources.GetObject("logoPictureBox.Image"))); + this.logoPictureBox.Image = global::DiztinGUIsh.Properties.Resources.diz_4; this.logoPictureBox.Location = new System.Drawing.Point(0, 0); this.logoPictureBox.Margin = new System.Windows.Forms.Padding(0); this.logoPictureBox.Name = "logoPictureBox"; this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 7); - this.logoPictureBox.Size = new System.Drawing.Size(136, 229); - this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.logoPictureBox.Size = new System.Drawing.Size(256, 326); + this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.logoPictureBox.TabIndex = 12; this.logoPictureBox.TabStop = false; // // labelProductName // this.labelProductName.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelProductName.Location = new System.Drawing.Point(139, 15); + this.labelProductName.Location = new System.Drawing.Point(259, 15); this.labelProductName.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); this.labelProductName.MaximumSize = new System.Drawing.Size(0, 17); this.labelProductName.Name = "labelProductName"; - this.labelProductName.Size = new System.Drawing.Size(199, 17); + this.labelProductName.Size = new System.Drawing.Size(414, 17); this.labelProductName.TabIndex = 0; this.labelProductName.Text = "Product Name"; this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -95,11 +94,11 @@ private void InitializeComponent() // labelVersion // this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelVersion.Location = new System.Drawing.Point(139, 35); + this.labelVersion.Location = new System.Drawing.Point(259, 35); this.labelVersion.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); this.labelVersion.MaximumSize = new System.Drawing.Size(0, 17); this.labelVersion.Name = "labelVersion"; - this.labelVersion.Size = new System.Drawing.Size(199, 17); + this.labelVersion.Size = new System.Drawing.Size(414, 17); this.labelVersion.TabIndex = 1; this.labelVersion.Text = "Version"; this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -107,11 +106,11 @@ private void InitializeComponent() // labelCopyright // this.labelCopyright.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelCopyright.Location = new System.Drawing.Point(139, 55); + this.labelCopyright.Location = new System.Drawing.Point(259, 55); this.labelCopyright.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); this.labelCopyright.MaximumSize = new System.Drawing.Size(0, 17); this.labelCopyright.Name = "labelCopyright"; - this.labelCopyright.Size = new System.Drawing.Size(199, 17); + this.labelCopyright.Size = new System.Drawing.Size(414, 17); this.labelCopyright.TabIndex = 2; this.labelCopyright.Text = "Copyright"; this.labelCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -119,11 +118,11 @@ private void InitializeComponent() // labelCompanyName // this.labelCompanyName.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelCompanyName.Location = new System.Drawing.Point(139, 75); + this.labelCompanyName.Location = new System.Drawing.Point(259, 75); this.labelCompanyName.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); this.labelCompanyName.MaximumSize = new System.Drawing.Size(0, 17); this.labelCompanyName.Name = "labelCompanyName"; - this.labelCompanyName.Size = new System.Drawing.Size(199, 17); + this.labelCompanyName.Size = new System.Drawing.Size(414, 17); this.labelCompanyName.TabIndex = 3; this.labelCompanyName.Text = "Company Name"; this.labelCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -131,12 +130,12 @@ private void InitializeComponent() // textBoxDescription // this.textBoxDescription.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxDescription.Location = new System.Drawing.Point(139, 98); + this.textBoxDescription.Location = new System.Drawing.Point(259, 101); this.textBoxDescription.Multiline = true; this.textBoxDescription.Name = "textBoxDescription"; this.textBoxDescription.ReadOnly = true; this.textBoxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.textBoxDescription.Size = new System.Drawing.Size(196, 93); + this.textBoxDescription.Size = new System.Drawing.Size(411, 189); this.textBoxDescription.TabIndex = 4; this.textBoxDescription.TabStop = false; this.textBoxDescription.Text = "Description"; @@ -145,18 +144,19 @@ private void InitializeComponent() // this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.okButton.Location = new System.Drawing.Point(255, 203); + this.okButton.Location = new System.Drawing.Point(590, 300); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(80, 23); this.okButton.TabIndex = 5; this.okButton.Text = "&OK"; + this.okButton.Click += new System.EventHandler(this.okButton_Click); // // About // this.AcceptButton = this.okButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(338, 229); + this.ClientSize = new System.Drawing.Size(673, 326); this.Controls.Add(this.tableLayoutPanel); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.MaximizeBox = false; diff --git a/DiztinGUIsh/window/dialog/About.cs b/DiztinGUIsh/window/dialog/About.cs index a190b92f..b6dafbbf 100644 --- a/DiztinGUIsh/window/dialog/About.cs +++ b/DiztinGUIsh/window/dialog/About.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Linq; using System.Reflection; -using System.Threading.Tasks; using System.Windows.Forms; namespace DiztinGUIsh @@ -14,12 +9,12 @@ partial class About : Form public About() { InitializeComponent(); - this.Text = String.Format("About {0}", AssemblyTitle); - this.labelProductName.Text = AssemblyProduct; - this.labelVersion.Text = String.Format("Version {0}", AssemblyVersion); - this.labelCopyright.Text = AssemblyCopyright; - this.labelCompanyName.Text = AssemblyCompany; - this.textBoxDescription.Text = AssemblyDescription; + Text = $"About {AssemblyTitle}"; + labelProductName.Text = AssemblyProduct; + labelVersion.Text = $"Version {AssemblyVersion}"; + labelCopyright.Text = AssemblyCopyright; + labelCompanyName.Text = AssemblyCompany; + textBoxDescription.Text = AssemblyDescription; } #region Assembly Attribute Accessors @@ -28,37 +23,40 @@ public string AssemblyTitle { get { - object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); - if (attributes.Length > 0) - { - AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; - if (titleAttribute.Title != "") - { - return titleAttribute.Title; - } - } - return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); + var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length <= 0) + return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); + + var titleAttribute = (AssemblyTitleAttribute)attributes[0]; + return titleAttribute.Title != "" + ? titleAttribute.Title + : System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); } } - public string AssemblyVersion - { - get - { - return Assembly.GetExecutingAssembly().GetName().Version.ToString(); - } - } + public string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString(); public string AssemblyDescription { get { - object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); - if (attributes.Length == 0) - { + var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + var assembly = attributes.Length == 0 + ? null + : ((AssemblyDescriptionAttribute)attributes[0]); + + if (assembly == null) return ""; - } - return ((AssemblyDescriptionAttribute)attributes[0]).Description; + + var description = + assembly.Description + "\r\n" + "\r\n" + "Build info:\r\n" + + "Git branch: " + ThisAssembly.Git.Branch + "\r\n" + + "Git commit: " + ThisAssembly.Git.Commit + "\r\n" + + "Git repo URL: " + ThisAssembly.Git.RepositoryUrl + "\r\n" + + "Git tag: " + ThisAssembly.Git.Tag + "\r\n" + + "Git last commit date: " + ThisAssembly.Git.CommitDate + "\r\n"; + + return description; } } @@ -66,12 +64,8 @@ public string AssemblyProduct { get { - object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); - if (attributes.Length == 0) - { - return ""; - } - return ((AssemblyProductAttribute)attributes[0]).Product; + var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); + return attributes.Length == 0 ? "" : ((AssemblyProductAttribute)attributes[0]).Product; } } @@ -79,12 +73,8 @@ public string AssemblyCopyright { get { - object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); - if (attributes.Length == 0) - { - return ""; - } - return ((AssemblyCopyrightAttribute)attributes[0]).Copyright; + var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + return attributes.Length == 0 ? "" : ((AssemblyCopyrightAttribute)attributes[0]).Copyright; } } @@ -92,12 +82,8 @@ public string AssemblyCompany { get { - object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); - if (attributes.Length == 0) - { - return ""; - } - return ((AssemblyCompanyAttribute)attributes[0]).Company; + var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + return attributes.Length == 0 ? "" : ((AssemblyCompanyAttribute)attributes[0]).Company; } } #endregion @@ -106,5 +92,10 @@ private void About_Load(object sender, EventArgs e) { } + + private void okButton_Click(object sender, EventArgs e) + { + + } } } diff --git a/DiztinGUIsh/window/dialog/About.resx b/DiztinGUIsh/window/dialog/About.resx index e4963227..1af7de15 100644 --- a/DiztinGUIsh/window/dialog/About.resx +++ b/DiztinGUIsh/window/dialog/About.resx @@ -117,591 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - iVBORw0KGgoAAAANSUhEUgAAAIgAAADkCAYAAABDjGxzAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAL - EwAACxMBAJqcGAAAh/9JREFUeF61vWvwfNFVlpkPM6IIgjdEEINch0EpEBFhcBiBAR3Ly6AUjlKog1Iq - OjgWyIDKoFRQA0gFNBhFoQIRpAJDDCKg3BRMBRDUGJD7PeQmAQQE+fCb/ax3P3ut0/37B4TwYdU+ffp2 - +uznvOtd65zufspbvOfXPhhv/ju+ZsVXV7zZu3/Zid/w2/954t2+qOJN/6fn7Xj+w1Pf9f97eOq7PPfh - 1/+2z3349e/8nIc3+a2fueLZD7/uHf9+4rd82sMb/+Zn7Xjmwxu/w99+eKO3/6SHX/t2n7ji41c8/eEN - f+PHPrzhb/oba/y4Wv41/+PH7Pjohzf4Hz7y4Ve/9Yft+PCHX/WWfz7xFn+uxl/55h+y4k8//Irf8CdW - /PEVH/Twy5/6gRWv/yZ/aI0fsMb3r2XG1/t177fiD+zlP/Dwy97o9674PQ+v98a/v4Ll133D91nxu07U - c974fR9e5w3e6+F1fvW7P/zSX/Xbd7zbincd69614rV/xTut+K0Vv+SXv+N9vP477PHtKn7x6/3Gvfz2 - uf3L3nbE2zy81uu+9WU58ZYdr/MWJ37RL32zFW+6R5efeon//pe88Yg3GuHtNzzxlLd4jxc+vOV7fcMC - 5OsDynu8oABhFI43e/cvH6B88QLjCzcozz+gAMhT3+XzFiSfXZAARgD5h4m1XID85k+teKO3/+QCJbAA - CsAAy9Mqfs3b/tWC5A3e5i8XJAHlw1d8RMWvessP3QEwH1qQ/Mo3/1MblA/aoPzRDcsHBJRf/4cTCxYh - OYAsCGr51/7uSwCGUL3OG7zHit/R4wZjLgea334BJMD8tgHIAuEAsmDZYGR8hwPMFZC3Obez7tUB8maX - 5UCyQXntN7nEFZQG5L97rTeocSnIAuNAopKgIlETgYmafHkBUkriuEEBjKjI55SCtJpsRRGYAgVIgGWp - SinKMwYkH3+BJIB8VMe6HVCIKAtqEiWJmjwZlMDSkLzfUZUat4IcUNbIep4HICgLcLzuG773BuUaAUQl - ebeCYirKAaTgAAyVRGAGHGtsFWkwOt62wLkF5WcExLiD5AmAvNV7v2irx9cvSL5xQ/L1D2/+v/zLwLHG - N3v3r9yBikwwnlcp5qSaFQBSsJwRRXn2hmSryVaUVpWkngkJo+km40o3C46knY8qFfnVb/UXhrKsWLcr - 9RQwQPLBG5Skn4ASNUmsNLRhMeUk1bxvIFmw+DggSsoBjpVqVrA8YSk1KSVJ6pkBLAmU5Z0KiBoLlKUe - BYiKstUDSGZsMOaouqggj4MCFEJyA8uBBDAMQXmjAAIYJ80sNTljqcfXlHKYZgwhIUgtSTdfuGMBs3zJ - 8SdLTQIM6pL0IzBv8k6fUbcLmHW7PcpSEnzJiqSbjy5VERIAiapsj7LgSPpZinJJOwTKAjAqimpyoyor - ChJSyvYkuf+PFkBAc+tPvB1oOvUQQtHAoCoBREhUkaMwj6SbBiRqcgAqUBKtJmsUlh2tJKrJhuMCCFDc - R1JMqccCZAERHwIcLyz1iIJ8+R4FhFSDH/mSHV98gWaCospoYgMK6SeqElAWIBXbq5y0g4p83BqfXlFm - dt2u1IMvIf1UCtLIAk0URX9yTTsAgpFFTTLxKgRhupmh8piCgCHpJ2Y2I4BsMMbY6UZvopoICQBpaAcw - BYiQ7HRzB0qrxxWSe0AaksdUZI8XSFpNnvKW/+u/uwEEYDYkNWJWO8UkXI6yTPPK+ouSbBVJfN5WlKSe - BNBsY1sqEjXRyJJ+2sjqT1bK2f4ENTHtcFsVMZJuqHI+uCBxuUH5o2sMOEJyYimKqSqArNSz4FA95nKn - m6SfCywr9CdZji9xFJQGZKrIHAcsCxKBmIBkbE9yTTnAcKsmjwHS8RTSS6UZQWEUjg1ITGogUVU0sq6L - krSqAEigsdJJTDUpWGp5AVKpZqUcTO1aLmUpUJ5V423aKTUx/ewUZHkcFRGUmFlL45lyGAMKgMTUVrop - T5IS2cdGUeJRYmRHoCgFCyYWb0La2d6kgEA9DH3JVJR4kwbkNm7UZKcawZiw3EfDcg/GFRAhmbCUgpQH - OUqiURWQa6opYA4kASRK8mX7MdvIAkulm04zRkriKIreJMudevQn3UPp0niC0qUx6ScRRUlp3LDEyLY3 - +ZBT/URdUBb9CaknyuJjgCX+BEhW6VsRn8Jy0syEAz9yVZMGw5TzbnWfXqUAmXEAAY57FanlA0ogaSW5 - qXI2IPdqsgEx3Rw4hkl9q/f+plIRAKmxFGSnmg1JInBEVSiDVxQos7n2laUgASX+pH3J8/fyAGanH1NN - QYKCqCIY2gLlU7eSxJtc0058yUk/pSKjNN6+pKqcpSQBBX/ypwuYAAIoqXZMOaw3TakqR0k0s2tUSfQo - KYVTElcASClJ0swM1wtJUk58yT0kgaPHva4gSbRy3CjIhuRxJRGQq5KUgrzVe7/4AJJqBgWJkszbU1UA - A0iISjEjBQWS+JMCp1TlWgERSUdRGUFRTdqffPZRkqpwVJNlYuNNAss9JHRkb5ttExYabal0kn664QYk - 8R0ZC6YFSPzJgqQqnfRNJjCWxQQK0mknwFS6OWPHVVHiSQDjeJIDiQpyryQFh+NWkBm97jFIJiwbkD0e - QEgvPX5TjTbObscAYgOtW/MBZitPjTx2e5q9zHqVJ3CZlpbRBZQysZ9XypGUo4l9TgEiLAXMUhNBIc1E - UVCWrn5UE/2JoCTlbI9yVCWgxHOk8uE+nqu6tD+ZrXtTjv2T9iYxrkk5hqqS5elRJiTpwl4gKVAmJFs9 - JiSVapJuDKB4EiD3ajIA2ctPeevf+S2lHlNBnNTc/oYzqiyZ4EBQy2u8Ba0fP0dBifokPeW1jrLsdDRT - EKqCec2IR9lGtlJPTGzBMsrjgqTMbLqypSgFS6efBqWB0XMQ3AYyQLHiSVk82/apeEpJCpDfW7AUJKfK - ARKjIblXkIwX03rgMNWoJhOWrShAUbA0JFcF2ZCsaDAmIIwbDgF5q/f5j6UaBQgTW5MbSASnJr0gMTLp - TPJ53HodYHub3/Utex2pa77mBm3fLsAqAguvByxJR19efgVAChaUZS13CjINLWBUk51+YmSJrSQFCWqy - 084CpJTF9HNjZivdLEgIIOI5KIzeREAq5WxI2pfscZvXSjsXSKIiwBMDS5qJiUU5Wk0eaaiteFLaUUUO - KPu2UDwJkGu6eRIgNZEAcp3Q6/qVdjYcqkFFPS5w/Kbf860FyNv+7oxRpjzf5x7YAETAgG2CskLvMiuh - Sjmj+illmSXygsSYXuWacoY3WWGT7fRRCpJdEq8omBZgrIuBTSncsERRSkUEhXSzITlqciD5XYFmp59A - spRkgaGa9LI9kmFYDxyAMZXkCkrFVpAs3wKyxwsgBqAYT02KORNdETXJuGMB4GNqordaAAWKwQgYrGdk - 3bzf5X7Nhk81YjRdBR78iibX1LOqnwUHqiIsGeNbDiin2YaifFKBkrSzy+OlIJe0s82t5XFK4qQXAUnq - 8bKCnONJSXxtrgHJXBaUwKE3Md3Yom9QOtUQV0guvuSSblo5uH2FJL4EKBqUhmPGFZKRYm4hqeW1Xjgy - mVtd1kQysW/3e7+1Jl04XGY98fa/71sf3vn9vv3hN//v31bLgMJ6HitERN4z6kUAR8YY4lIU1eQ03uzQ - RklUF31JINlt+60k6aM8o4BJeTyvQ3naua2qoCg+zv5JTKwnAYVk+JIyrprXx9QkJlY1SaS5BhCaVmMa - VqAQjhovgBC3wCxQhoLcq8iIA4dphkiaKUACR6RfYAJGr6vJ3CMTLgi3UBBCwbp3fN9vO/cLBiO3hcp1 - BcrenhM7BaEmp69SoNjO7y5tpZxKO91LQU2AJLB41jgmlskHnCqn1/2s63TzUSc9qSCknVnlBBJSzu7A - lmm1ytkVjlGwcM2JKnIF5KjIMa9RkakcgnLSzFzeULh8VRHjCspUlAbj2kw7CpIjOLLPbcIjPPdzlEc5 - nHxAcPJdfqc/+G0FxQTDxwsGtydYLh+lOn6lg3VtYmnGdQPuoiy76ilIqvJpWOJNuBYlI1CgTKgWr8tt - Uw1pJyX0J+60k5LYSOmb8zutJqO6KVhuUg6QbBXpVINxBRQ9CYF5NeWk+zq7rSpIp5ukmAmH61wWkMfg - UEUq9m19yFOAQABqRDmIdZuJqwnaEDHBKgQgzOV3/UPfXimF5QmOj5kwCAzrfT5AsS3cz/v4foEmvRkm - MpB0Q+403Pb5n/gUu7OWxzkZaPoBEJZ5PK/NdvPePBeFIe2gLqgPKakUpU4E9rkdIGlfEkAshUtFbtQk - gOzKZhtVbqskGFngCChporWSkGq8+OiRtjypxXFFA7KWp4Ks9VdANhhCYpSCDEDYSYEknoN1tb5AyTru - ZzJVByeXnQscTLIBAD7mFpTb5wsHI7cFg8i2Je3UdixY8SUBxO5szibPHoopp0HJZQYFye6jcD/+5l3e - /9sffs+f+u6H//mPfEelMrwLqkF6YZlR8xoloV+SVn1Sjif/BOWDFyQxr3qT+JOGxLgqSUxrACHNtC95 - DJA7byIkBUqM6lGUCckCwzFABJiKrSIz3awylxIW4xlIPIIFw3CSnUyCnTvhcNJ5DOBMpWCZdQLCMsH9 - vA7h6whHKdh6b4Fl+1ASq5tUOLO9vxVlgXLKY/on25doZhkBi9f/3/7kdz38kQ/73off92e+u27zvKqC - FhzARZqpSgdISkX2NScoCJDsnsk15VAG21jbLXrNa6WZBcjyJQXIUo4rKPqSeBFGVWSmmniSnWKE5AaQ - GTPNTEgYA0iU4wrI9iA5Ul90jlwmwkkpQBYsTCSTx+Qy2Sy/2x/+jgMIky0gPpZ1jELj813vc1gWEoLn - M1mMLrMtAoMfsbpx1JsEki9JFCSoSZ9RZgQiDgre6/d/yHcXIO/3od9T28Nr6FsACUDon5Qv2SWxlY7p - JtUNShI1SbrpnslUkZjWVDgn3QDJ6JXElyTVOKb8VUGmcR2gVIrRqBrxIScKjBW1fDWtEw6Xq1EGEIwe - pYRHsctMJDuUncikMdEAgjSzHlCcYEGYMNyu4/EEz+c2o8DxGN5vQgKUbg/A2jupJpvl8PInqXbiS1pV - UvmUsixweCyv9Z5/7Dsf3vfPfvfDB3z49z78H3/he+s2r9kwPb/MLD6k+yfjiraCBF+y0k4pSspgFMV0 - E0ByCYHG9YByUs2G5EZJ2pNs47pMayuIKadTTSvIPShHRRyHmrxaQNhR5nknAyBUFNWDSXOiCSZTQISE - iXZyfRzrXO86HisMLvs6vq6vx+N9LiPQBJJUNwXKNrATlplyspwr8gEICHg9vAdg/LH/5/tKRX73B39X - gaO/ARBMbXonXPbYjbZq01fKQUUakKQbLkZKWAJHQbgIKSb29El2yhESU0yWxwm+0R9pOFSTkV5W1O0D - SVc3c7wHZKeYY1K3B3Fns+OV8oKG1LKC+5kYwskGAMbf8YGZTNcTPo7JFgZuO7oMHIIxAXn3D/iOel2W - fRyvA6S8LyHA3SvpSxICzdde/El5llKXryk4+Jy85x/8v77n4QM/4vsePvivfP/DH//I7ys1QakC2wsK - rlQ9ubTAJhuQoCKmnTavGxR8SakJ6UY/AiR9/uaSbraaqCDA0ulGP7JU5JS9HXcKMs1q3QYKYCF2urlJ - MRdIDGBZUSYVCAjhuAVFRfBIJtjBhBPPRAqOk8v9qoSjjwUCAll/7//zOx9+55/4rrMMJELjsq/B84HF - bY6SUOHQp7mWwz3mcgWC5/B83uf9//z3PPzJv/z9D3/mr/7Aw5/6f7+/1IT3qM+9Xhe4vGip+id1bqch - iTeZlxG0msSbUOXk2hLgmLAEEODYgBxIUgIDh6AEECubnMwLIH3ml3GCce9NAoiQ1KgfuQCy082GJCkG - tdiphB3IEWpwH0Aw+UySoDhxThrLBPcxznU8xmVeg4mfcLzXGpF3lqkqWC9AU1UYCd6D7eZod3vTXLMk - zgnCqEmUxfuBn+3BnKIef/av/cDD//3XX1IjKsK2eICgPFQy1x5Kmmdpw3Oh0j4zXL7ESwiEw66rSuIJ - vulJRueVdLNAEZKkmTTTjh8pFbkCclWTa7oJIKaWDcgBY8dWk8dUZKeYGNUcOQHFnc064GBShIFJdqJZ - R3iECxGPI5jQOclAMpVCOCgz8QSMQML9Po4QGEEBDraTEGYmNp8nPZP6DGPkfraN9yS9fNBf+r6HD33a - Sx7+wgIESFARtoXHVKpZngYfkh5KTgamE2vK+eQFyfi6aPVL9llhVWSUwN2Wv22m7RJ4+JJjWocviVk1 - 1eRyxSjINKwqyk47J8W0epw4kDziRYh1+6QYdrRHDmAIDKPqwYQDATvQyXbCgMBgPet47Fzv41QIJgoo - OJoJJo1yk2VAYbJ4LI8jAMVUZLpj2wSFdUJSoKAmpSjfVI8j2DZel3RCavmwv/mSCiHBh/BebB/Pw4dU - s616KfP6k76IujzJLoH1JF3hCMlsqLVxNd20ilDZdAmcVEPK2dXMgsKKJqC0enTKyXmbhuQ+xRxABigX - BZmAEOxYQzjYudwWEEdhYaKYcHamAExYWGZne9vJZpKZJEIw8AMEEyckPI4AIpWE27wG8Jmy2Ba2DUAI - geFzsP0sq2bCgfcAiL/48T/48JGf+IMPf+mTXlrLpB0ew3vxXDxMqqEvrr6IaqIv6YuT+stdUZEoSdJO - WvOtJHRb51lgS9/tSTYkgpI0k1QTULoVr5okACRgPO5BiKSbA8kBRAUxAso5WQcQyLSjO5dl5JYdLBxM - DqESMM5lRoNJUVGEgxSCQtiDoIpA7pk0Sk7WTyUhWBYqXoPXJACPbeG9WGY7CUABGlRGxeN9eW0g+HMf - +wOlGoDxV57x0oeP/uSXPnzEJ/xgbYPvzee3c0tFRA/FzmwpyW/JhdQqSanJSjVAYbqJiuBLcvFzlCTl - bwzr9CKWvzGrrST2R0w1wJHSt9vxt78o8FhvZEUt33daVZErKABS+fmbCgSPPHYMITysYxLY8exsRlVB - CFQIR5WD+3iMcKAGjEy0JhGJZ6KYIDwBkDBJTCYTxcQyGtzmNQ23g/fg/VQX1nufUKIeeA3gQDEA42M+ - 5WUPf+3vvKxG0g7QomYcDHx+q6F0aLeSVMrZ3wjc155ESbZxJeWcdLPNqyXwinlyL0rSFx5dK5odeJLt - R45hrcqmlWT6j4uCXEBBQW5U5MDxCCAoBjAIwlQRAGEdRyJQMLLzVRGPYibAFCAgKomAAIVgAAlphaPV - SfrYZ77s4W/8vZc/PO1TX1awAAqTBURAATA8h8ljonkvgGBk8hl5bZZ5b24LjffxGsAHILwvysH7EX/9 - WS+v7SDtsF28L8+v/bAOIqqg9FbSyq/zPBsUTwC2gUVJctUakAAHoOQEn820Nq6BJHFUpGKWv7v03ZVN - DGvg6BTTlcydghxARjWz4DigbDgExPEpgoCKAMZMNaxnBBKl20BJAEUgmAiCnUoIiZPFxDFJpgsmGwAE - 5G99+isenvmPXvnwrM/5Tw/PePYrCxi8ARPG0SwgjLwWAWi+psFrAyLvyW1NMM+lW6p6oBbE0z/t5QUH - gYqgZqQfS14OilLTDYldWsyrZ46jJjl/MzuvlsB3KnIgMd3oSZJugOOoSXmRtORVlelJyqyeFJOKhmg/ - MnzIBZKRZoZ63AGCclSaWTAABqGSsGNcBghg0Ie4jJKwzISwQ5k4lYJ1AsIkEU4Yk84kMFkcyUzUs7/g - VQ//+It++OEzPv+HChjA4X6OaJSDSfZ1CG4TGlxTEu9N+DjuQxF4HZSJVAaAvCfvY6Ak3EcAEtuKQnJA - ZD+lr6JxJXIi8PkxrguOOhO8082scAAklc0tJFNJ0o6305p0gye59kdaQXaH9ShJmmgNCYrySKopPxJI - rqDMziomdQOSD5/T+6qHacd1gCIcphfSB7cZVQsmhB1rSgEYj2xHlYAjGoMKBEzY313q8flf+iMPn/vP - fvjhE/7hKwoc0g0+hQk23RA8n4nnNTC4eAshma/Pep4HjMCBKgEe6QwoPukzXvHwKZ/5ygrWoSq8JyrC - awE3BwBGPRXfhCTnd64Vzq0nQUl2x1VAdulbUS15z9mYajYolW52mrlJNQ1Ip5mY1nnWt1PNJe2MambG - VVECyqWTqlkFDIAwBAYVAQ6OKAABDkbWqRRAIQzA4YQyaR7h3OeRzySqJMg8kBBMFhNJCuJo5nGaRycd - PwEcPJ/gfoLHcr+VEWHVAnS8D2CQyv72Z3WwTk9CauN12FY+m+Uz+6FSze7Q4kviSfJlLyARkNlQ048k - 3aSRVipSgKAiXgA9uqy7ugkcu6LZDbQueXdv5KSaqSJtVi8qcgDZZhUgLqlGQN72mmKsaASCHcL9LKsi - QmIACEfYNKKkGsEwpTAKyfQi3GbCmUykHR8gHEwm61AZJlmYNJqEJ9q4n1Gl4fW4H58DHHiLmVpQDDwP - MP79z/1Ptcw6VAX1QkV4bbaN7fVAwJOwj/oMsp6E62JzmcCEJCXwAsRmWpW/3UgLIPMCaFvysz/yGCDj - JF6pCBcW5Xs1VxVRQZ706wFTRVSVhuekGGEAFG8LCXAQ3OZIAgh2mFUCI+s40vQgt4rCTlZBCGFxnT4B - xSANMJkaRoAgAIAJJ1V4DoVlHsN6gkkVHu5DNQhej/SBeqAcgIEhxu/8g+f+UEFCqCaAyvMBjW3l8/A5 - Uc9KN3VA7XRTKhLTCiCBpL/QpR8h3VwBydcpZgOtU8217K3x9EU82xsVmVUNYAhJADHVPKIkQrLGY2jX - 7akm1Shj4usDrwAEFEQgWBYSlm2aCQSAEKrHBAMoGFUPQLj1IXPkaAUEJpnJZpJnCkElmGzAYAQmb5tC - GAkml/tRAkJlEg6g+PQFx2f9k1edwCQDDcFjeT6vw3uzzXwWPvepbNb+4gxxfS3jUgJ/YalIlCRfu0BF - zqUCpJp9+SKlr11WU036I7mutX1ISl/gwIsEkPnFq3iRVpBZ1Zhq5jJj4gLLTDsAMkFAORiFYoIx1wEJ - SqL3EBLBmLcBAlBMNQbKQcoADH0Ft4FFtTBtAArLqAYQEKiM4bkUJtRUQpCiAMMeC8pAKkE1AOI5z3/V - w2d/4avKFFM9sQwk3E+a4bnAx3vP/guQsA/YJ+d6lJVqVJKkmz7Jl2tcFyjnOzkqSUpfVcSStxtoXs+6 - q5kBSSvIKHkvkNh6F5KUviiM6aehSFx8SC0vk8qE80GFo9LMyrFAAxwzJjAszzRjahEMYWGnqiI8RiWZ - qgEYBJOg+dRbMJoqVA4UAY/CBDJOWASDdAIYTDS+Qs+BagAGUHzBv/iRqpoYn/dlWWY9oPB4vAqvyXsC - Kdsq4ELC/ihPspQklY3XxcaTBJJcTZ9zN91tTZpJ2RtA7IuoIDcn8rYXSapJNdNpxs7qVpMDyf7G3o7H - wEA1CogVpR4bjgJkTj4Tj2wCiesIYCFul9lBQiIUqAUxb7NDAYVAOYRjKgdwCAZpRRVhYkwfKAST78Tj - J6g4LE0BRsVgParBRKMcpA3UATie+yU//PD8r/jRin/6lT/68MX/6kcfvuirMgoNIPFcXovXBk62jW3l - M/DZUFAqOfYFkNyWv2Vaq/zdv2/yjjGtXktCqpl9EdNMIEmqOdXM7o2UipymWczqBCWAAErDcjWttx5E - Q5rUcgDZUYBc1cOKJiUvEAgKt4HDdSx7bgblQClQCVOKZS7LggIc7GBN6QQEGBgJ1jEhBGnD1IFiaDYJ - JpDbgMERDxiWsUwwfsOUQhoBAMD40q/5zxX/fMWXvSDBbSDhfkDCp/A6ltv4IqBl22e6YR/UwTVMK5Ck - 05pv+0VB+EZfWvHVH1l+RBU5X6HYFxqRauyJlJIsQICjIEFBTjXTXoTxnK8ZCmKKad+hB4kpDRyOG5Kd - ZqoPwmQ7CkHBso2riiEcButctomGJ5mgsCMBg3VCcqsejOx4RgFRPZgUJgcFMW0AAOpAAIa9CwBBWYRD - v6HPAA6UAij+5df9WMW//sYff/jqf9PxVV/7YwUJKmKqUUkAFDWzsuHzCAn7wgOrTGud3KOy2d/N2Sf2 - 7I0kzXzcqWa6L9KAXLzIVpCriqQNb/krKKoGyw3ILRwTkBs4KsVskyoUBJCoJC6znscIwxw5chhREOCY - PRK9CTuQZXYmMc0q8JhuGAkgsY8BHHoORtVDxRAMAljwGpapHP3AgWow2agCE49SAMIL/92PP3zdi36i - 4uv/QwfA8JgJCSrEa09I2EYhsfylumHfpUfihdO2458Xw/qOXrY4O6x9zUgAwYvYG7lWNAGljWsb1oaj - 0stQEI1pp5kNx6MpBlgalALEQCFUEkcB4T6VZF6UM6+7MCcLDP7EoB3PkYbCsIzCAA/ACIqpRjjI+wST - YooRENUEMFQOSlhUg34GfgPVwHwy2aiGYADCN7z4Jx7+/bf8lxMv+tYE61+wIPmKF/5YqQ2qQ3rCwwgJ - 28L2sa2kS7YfSNgX7CMrG8/Z+AWuU/bycxS7ogkk+WnxAqTOz1jNpLsKJIbGtc3qPHmXUDkaEuCwogkY - QBJAFhCOLo84Za4wAIYweNuRxwACRwnLgCEswCEYhEoCHIzzvA1ByjEVqSikHSAhvQgJaYYUg4pYvVil - mGqAQyOKcuAfOPo1oKQN0glg/NtvDgj/8Tt/suJbv7tH4pu/4ycLEpQESPAlmFrUCCUBRtQLRcMfqSR8 - Dj87++ukmqEiXhrQbfhc09p9Ea6K39/73edqhKVLXi8q6s5qwLDktZrJ+RnB6BL3tg3P+MiXwVUQQWCS - hUBgpooIDVAQrGck3QAAt1kGEm6zo4AD1XAk5QAHygEcqoc+RW8CHFYvjpSb9jWsWJgs04qdUY52VQMo - VA3AePG3NQzf/j0/9fBd3/9TD9/xvRkNgeHxpB9AQX0ADlVCoXhfQAUSTySy/ajjnWmtVnwMa3mRBUma - Z1w78qk71eSCZ1QkniQt+KkinVq8XsSTd+0/rGQKkqUeDYopJg00b19SzSNwMFaKmZA8NgoHt328t1UR - doqjR5JwAIzLmlhgIcjhAIJUs6NRD45M1IOdb6oRitvUQlpBNQjgwDMwoUyu6YQUgjIw+UDwPS/5rw/f - /9LES17+0w8vfeVP1+ht7uexAKWa3ELC+wMskAAx3onPwWfjc7Jv0h+5phrK3sdUpCuaGNY2qreXJmpS - cwnAgeSUvERgKU8CKCfVAAgK4vmZ27O7jsODQPqcfAIlMLjNY0grQsP629vAAQgCIhAqCYFyoCBAQs62 - 0lE9NKcoBmFjTO+hgjA5s1KZzS/MKCZT1SCdAMdjULxsxyt+qEeC+3icisLroEYaV0BEtdgOlYRuq5Dw - OdkX7Jf0R/rroHVpwFKSuizgQOK1rAuQbVRJLw1IvnhVgGwlOe334UW67O1UEzCiGkktiQsgBYmAtIIU - ICgAgDjZpg9C08UyoynH2z6PHcHzZjXDyG1AEBbhIDSoBHBYvaAgqAbBjkc5qFJQDWJ2RQm8gWbUSgXv - ICBM8Pf94H+tSTcmDE8KHqOaABhKJCR6EiDB/7CNQoJxBXjgBxL2S1U2+xIBv1yOJ/EyRcreKnnHrwZQ - xQhJSl3j2mF9MhypZlo5TDPdZr+kF8A4oKAgjG/fgDDpUxEEwXUzbrutvAbPAQyWGQnWAQlgmFIAA0BU - DkI47J6aVlAOvAdgMBGAodegUpllLBNHYEhJCUwoE4sKAMUrHoHgZxNAgk/Bl5BugATzy3vabZ3phm0H - EqBXMdkX7GP2Gy15O63122i7w2r7PV4Es5qLimZX9aiICnIHhw0zAYlRvQelK5qC5CjHjIByAYSYkKgS - 3L5Vj/lYoeA5+hDAYD3xmP8AEHYiVYtg4D3YyYRwIOGmE80oPmB2RlEPqg1SC3CgHJpR1eMVj0z+zzZ4 - vpDgbVAoIMHvACrbhh8CEkpgvBOfx5TD52WfuB/tk8S85ppWv7VH6et1rPZEcgnAVg59yPYeTzKqKsgE - RTCiIjfmdMaBZAHCpDKJbDzLjIa358hjGYFkqoswsCNIL3oPZJYRSIBD5aDvARjIMkcdnoOTclYrqMZt - lUKgGvY4SCmmFZWD1GIZy6TiJX6+gBAoCcDx2rwPQAKmkLCN9kkoy4HEMpjPy+cm3bIv2E/sv7d67xdX - KZwymD7JcxYkz9qlb6oaIJkKcvog26AyXgGJSQ0cNM5iVPUhppiL/7gDI+pRHoQjnglmgwXFUCmmYtzC - ITwCwusxAghQoBqaUfsc7DSg8CwtRx15HCNqV9QKhaOTCbBK8WQbEi8cVBgAwtE91QP/wMT+bDzHzxRA - Nv0I7wckbAeQkG6AhO3GSHsWGFA4CPjcVGrAwoHCvnF/53KBBiVq8sxT2cSLAElfaXZK3QIk3+GtsdKN - FxDdVjGBJICMFHOBJN5DUI6CcOQzudye0AiCKcbbQuOH5Dnz+RrUCYYpBH8BFCgFIRj6DFIKOxsT6llY - gpQCHBy59jqYJCSffgeAUNICCH2O10R6mQFsvC7vgVKR0oAUNWP7TDcADiSmHPs5VGiz+yooqCz7UI9S - 15WsklgTizdJX+SxTmqguAKSVHNU5ECSMjeQdKPsSQpyPIgqIBhCIwAqh3Awsn52VLnfADbyLh6Do0df - YUfUisROKNKszzCVaESFQ/XgiCX/E8CBemAcmTB7Hr9QgKBEpC36I3oRIDXVoCJAjfIJCZ9XSFATIEFB - 8SccOKYfDC2wcGCxT9m3VD34FDwKpTDp5raC6T5Ioo3qvAxRMHLirgHpRtk9KFGSAmSGcKgQM1AR1nsf - oDymJCgH/gIo2EEYTXYWUKAUggEUwMEORTEIwCA82TbhQEGmehAcxQCCL6DKwCOQXphIjvjXNCB4mplm - 2AZgZdvYXrYd0FFDPiPqSLoBElKqkGBiNbIcSIACJOw7wp4S+xRTS/oh7dBA625q0kzgGEpSfsRyN5A8 - yYcwXsA4ywMQ08NUEAMIVI4JjctzHa/FY/mgfHB2CNWIaURA2HEEO5GcDShAQqAegMLRSLDTAYQjdKYY - L/BRQTjBxqT9QgJCaFaBkbQGoADCdgExYLP9fBY+HwcDn50DBQXVl5BuAYSUIyT0hNh3qC9qgo9jDkg9 - AEK/hLLXUncqSRtVTarl7kozpJhd0dwCkuUbLzIVZMLBBEMtIxv2GAyuFx7vm2Uwr0X9T64lxbBDTDEc - TewwFMUGmNUKwdFHCMlUEiHxoh8AIc3M3odtdVIMR/trwqDO0IcAImmN9wZWUh+AsM1CDiB8xplq8F9T - RUg3VHQqCPvNLiz7lHKYphq9Eqqa9EVy5Xv3ROiokmqiIIHEFCMg11K3FaTTzBMBAQiVhA0TAm5PBZlA - ECx7H8vex/N5PY4CjgpA4SixjY6y2AgzBWlUgYUdyxFot3RCoop48Q+AmGaYMKoYPAIVxy+EBwE6FIT3 - AEoUTEBQOlIMcPA5gB/15MDg86IapF4Mu/4Do0qlp1FN15Vm2lcuML6wGmmUvV3N9JVmd0qyS99A4vUh - VwVhuQGZoEwFCRxlUoFAMDBIjE40k8yy6wwkjxE4NKqEKsJo/uSDk1MpdQl2DEcNRw87zJY6ATD6FVMQ - ajJB4QjlSKVyABLk/RYQ0wyQvCYB4bV4TeDgvWyYAQfwCgfbPeHgs1HFaE41pqSSWcHclbr8bjwt+AVH - Tt5R6ua8zKUfYpzOqteIWMncqsj9ZYhX9VBB3iFlLoDcphbWcZtlR6Hw9q16AIbheh7La5FPOVLYKewg - AmklB6MsgHKbilAXcvgEhUlATVASINGLAImlrr2Qn2+bfQbqYXrh9QGS90XNgIO0gpdie9luPoephM+o - YnCQcMCwP9g3KgYn8+oHaurnv/PLRfPrmumqeiFRzu5eeyKkmV3VDJOaVBP1uE8xj6jHDFIMG6oZuoWD - kdvCwMRDOvcJBOsFRTCEhMf4ugQQ0klkJ9l2JwWx49iJyC8pCFhmr4TUM6sdqxx9CUcwk8URjVnFQHKU - vyZVhNcAONUDIOfVZmwbyocKsu14rwkF6VZVZd8IBYqRk3c2yPiSFdespuWe78/sa0Tq3Exfr0qKCST3 - 14ncKUgBYrmbU/4djwCyleQpbPRUEIIPwYS6bBqZULBsTCi4jwtlfAzjBI80xsgOI9h5yK2qoqIgyXgV - ICHtGKYeQLFP4tlcINGwAglmlWoGL/LzMas8F9AADoXiPSxtbY4BMXCwzWw/6kgzjAOBA5DP7/UhOQ9D - KuEPG/NvFH3Czr9z3ZchVvgbZ7lWVaN6GmfHizQoqkh8yIqTYgTkZ1CQCYhgMHFzMm+hIFgWDNe7DpCI - t3rvF9f66U8IXlsYAUVIcO+AAiAcdUCCkSNfcyRyRJp6LJeBRF9in8TqBiUBElLBhOTnoiS3qUU4UC7e - W0NKSiSloILC4Um67BO+EuGvAeR34/06BHAkpQQMFeNcG1LfmclJuwmGaaYqmoJjp5td0XRHtc2qvZCf - Mc1so1omFcIZiakiTDrrhIYQFO4DBOAgSD3cFhxuCwy3BcTX4XWFhLRDXgYUdqzSDCAYO9Rk+hNyPHJO - vrfawQOoJkBCdYEnEZKfS9nLY3kOfobyGehQKZWD92YbgAPlAA62HUXUZ/BZ2S8535Kf9/arEH3RECfo - cgEzcASQ/GrzLRwxqDntrwdpH7KrmG1UpxeZSmIvZPZDAMRoBRl9EL0IH0g1cTIJoHBkwgWFZdWD28io - kBQo6/Z8HUIA5/sCCUfcNLE0j1QRADHtTDUBFE0sXoAqB0g4yplM+yNWNigJk/7q1ETV4LGARbridSxn - MckoB+lOOKjI2Fbb5qc6qSvK+mciCo538R/G82MzdcEQX+rep/m97DAKAiCmlKjHgWONB4wdgKGCEEAx - jepjXgQg7lTEFCMgegOCCeRoFwqPfKFQFVwGCJ7D6DrDdU8CZaYblUQVQapNOex8jlDVhLCPoppoYJlA - UgApByVhcjGWGExTDp4CCACFAApGoOA+wODxwGHFAngTDt6b9Mc2Tc8hHHx22uTnZzT3Nam5ur1/iShf - gbj/aYgC5EkXDZXvGBWMkAw4pno0JBrWlLtRkcdSzAbE6xMAAxCEg9tOogBwGzD88Ew8I7dZjmL0/86Q - Zmod6WcvEzyW8H1QEMCgi8hIZaN5pdKh0UYAy7xcgIlRTfAlTBrVDimHicSX2HG1T2KFAygoCrB4ITMj - 6YT7PWOL5+D5lLN4DtIK72Epq3KgeGwz+5PPxOevC4P2t+ymevQFQvlzRRUkX+jOj/BOOKaCHEB2agkk - wtFXmDUgt+VuvEhSzTSsKslMMQsQjl6CI5mJZhQUlgWGEARBQRX0GrmdP0B0vSM7CnBugfL9AIMdCyhs - C7c9EgGEMO2oLECCoqAkVDqoCSUxSmJJjE/QvDLJmldBAQJTD8CoGAT+hS4tqQrlwN+gHLw+7wMcpD7g - AF47ou5HPndSC80vLjHMT2f6uyEAku/rAkh+NtNrUqMglrX4D9TD/sf1mtQA0ipy7z9yEdExqjvNVBw/ - AigqyVATAGFSmAhGoRAMjgSPdj60IBw4AGJAERC8HSXJ4/JYnu9rAZywGLwv2zJTjievUBXb9gDCxJB6 - mCSOYvongGLKQU2AhJSDqbTCARR6GIBCz4SyFUjwKYDBeu4HDh7v+RWrFUpt2+ZAyvbcdUX3vkhqyZ8T - nfTyzp9dgNx+2z/+w19C9KsPlrT9DTu9R1UtM82MLqoqIhxREI1qwHhMRR6D5AACDEChYvBBHwOD5VaF - /ieFGtf6un+rBcvzudxvahKY+ZoTHmABEtQESDg6kXCOVHI9agIkKgmgYBb1JaqJkJAeUAJAwWwCAYpi - 6kExgMNzKzwOH4MKqRyYYtMKcLINwME2AjX7js/jFWJ+y786pPtLU7lQOT8o0+Vtd0xTvfjTVADS34+J - eljSpqwNIJ1i8CDlQ5aCzF5IANnp5eJBVA8BsapJyilAVA4gYXKcKCeTdWcij1KYRgKHMLiexwtJgFBx - VJQAVeAUZP2XJLw3wXaxfQYToZoACUcvI9WOakLKsROLkuBNgIRJ9jwOfkI18Wwwo6ph+9wOKU0wgNOQ - 0gjjPQGWbeIAcx/xOeqqsOU7oiBpiEU5Elf12Bcp75+kOoAc/wEgw3uoHhdAtkktQPoqs6hH+5CTYko5 - YlAFxLgDRFkHDo9eJ8hlJzkArB2wADmQONmMBcxcBxD5zQyOqlYd/kmbv2Ld/7K9n3cAW8H7AajboDch - UBNTDpMkJKYdJhBYONq9vIBSmF6JjbXbtMNIWKnwOEpnAEOV9BwoloZUz8G2sY18vvId7/m1+7O+uG4X - IKhHKUi6pmVQ98m4/oZ/4Lj6j6ke+g/TSyCJggjJrGL6upALIEdBkl4mJBMQ4tJqJ4TDCfKodjkTuCZ5 - TaoK40RGKdIUMgqO2nH5PznhmNAUJHXfBs3l9R5si/CiKEwGaqI/0cQyYSoKkHC2eJ7T8VICJn226Olr - qBqaUZTDtAIceo6ZVjTRfG73TX3OlVoItp37+Bz1HRjhqO/l+kMyppf8XllSzPzFIQ0qgORLU7cKIhi1 - vFNMpZcbBTn+oyKp5jbN3APyTn09CHAw8mENPnRBsT5s4MikcZ8geX/J6xoDwAZjqEQdVTfAuK7Wr53K - a/Acjz6CdWwXI8F7sq1AMnsnph9AsdpRSfQmKAnmlclHIYDBlMOIsnhu5bZaEQ6Vg/d3f/G5/QykFdIL - n4F9w7pUL/EeDQe/MkT3dLbX881+0kqrR1cv9+llQ3IxqEs9ChBP+WNUx7UhBw6VY6aaxAUQdjhH5W2K - qQ9eR3J+WJd17BDGUoqCwXSiL4mkEgEjfyTouuzAr6mx1u8xkPC4/m85g9vsaLaH7eC92Q4CUNh2AlCA - hAlEUVAT0g4GlqPfdMPE2ytRSTCjjEBjtWKfA89B7wU4AMNy/E451ran39F/G09b3ZZ6lbXnbG1+WHem - F0vbezj6GpCrB9nqsX1Hx04xZVCtYvQgXcG0evwMCuLOvoXD1AE4hEewihFAnMQc9dlRSR0CQYv5Ou7Y - OzTwoCZRHJ93lllf8PW/c7qNbPdMOSoLE0n6IRVQEs+vXKAKXj6gcQUMz+cAB+aWtMRzUCKVg5TC+/G+ - vH8dGMK9Pp+/BdJNMc7Upudxm17yI3adWhoQytoJx/05mEDyGCATDqGwgjHF6EP665iBIhFIAsgxqULC - h1cpWO9RwjrgaNXYYGzVQC0yyTFnTLBKETACB+s69n/Zrp1a9wPMWvZ+1hVoB5blYeb7r2W2S5D9DMKC - mqAk9k4wl3ZevWyAC3xQETwHaYXbmFLUg2pIQ8rrkM7YJ2df7M8vHP4jFWAk+IXD/GNmVS0bDq/1SGqx - tB1nbpc5tYIBkvQ/BiAnvbT/OBXMGk+K2ZAEDNOLy1GSqRytHkS33avMFQZGjxB3PDuCo1YFyQTFbHrk - C0cmk0kVCv4ufYFQE58R+T3jgCUA+QfIX312fEG2XpvbRB21TAxwqmQr2EZhRvH4HHgTfQlKwITTUMOP - oBKqCMEy0NjrwHdQFfFcXod94v6og2B/3tre/fm88CcNsdExXdHnXPbPTu3S1uoFUxow+mcwSTWaU6oX - o1PMhmMoyRWQ2SDb/uMsXyuZgGFq6XgKRwbBTnUHM5puhIOxJqVkVSPZBjQ7bE+oYOzgNkeYY+DIDs26 - 5GzX+9g8t1/P9wks8SaMUbF4JbdXSFQUlARPgnHFtOJHSDX0OYCDZeAgtVD9kJZIUTy34VgHx4KRz872 - GHyO8hzvlt8iO9d5bOWoWICkpR44+sRcfsBOODq9mFpy9XrAaGNagJRqREWOQV0xO6iqRpZNLzsuXqRT - CwpyAOHDAwKBajiyIzxiWjUyCSqIE+VR3oqQo0ogohj5BWJByO1133qsO5hgh5/lep7P6cfn/eJZ3IaA - srdzbbsKCOhMMp6EdGGqAQT8CHDYJbWkBSLSEs9BVd0PvGfeH2BzAo6Icux/n0I1tikVDKsWT8iRWmbl - Ms3pjFQvs7xdYEz/UcqRn8O0iuG2lYuANBxbQbZRbUCSajrFBI5KMcIBKBx13vaI4YgkSsqBYisHwY4q - QPaOy8SbOpxowVAl+jY5OzuYP+XJMjva2155leVAlNcFlFYXxkCisrVHAhQ+F5BgXlEReiSkGrwGplTf - 8Rc/Pv9NQ0mL4qge7IdzIKwA3GxTtrHK2N/2uduU5lxLeY+CY/yPndd8nB+K4R8xFyD7zC3R5jRwnBJX - 5RCUCn8nJO11U0wryCxvVY61fIFD9Zhw9MVDdVU7QLATXSbab0RWCxJ2OhOxdryToQdpQFSMKxRMsBPd - Y/6tyXUFR8l0Q5LlLeH7eVlmDCgVpVitLFlOc45t57NRhaAiAEAaAQrgwLTaLaVyofJRPdgP9TnX6/Xn - 6fMrqVZyCj9w9M9u4zkwpaYW1KPO2m7fARjdFENB2n8kxfDTl6SZcYLu+I9tUMt7+DMQSTHTpLZ6AMpj - qWV6kHtISkEmJCgHO+UCSEXgKBVhAjYUdUTtseFwR0YdshyXn0nH5W8IOMu5dnSUIlLNekYe16A8b9/O - Uet6J4z3jbpsSGubtrqgKgtyPiemVRVBMUgt+BHgoHIhvQARVRD7hOcB3YTcbQsUMaPAoRlVOShl+0Lk - p13Ug9CYxn+YVvQf/i7qKG2NrSAHkK0ilWYOHN0DCRwbENRjjQ1HFMSYgFSKYacBBlJ88SAl0/EdAnJS - y4KjgqO0IhORCcqEsUMda1JLGVL+Meboy45m5Cicy+z862Pzb07zOYwBholDUQKKcBLCC9zAT9qgbMWE - AoWpxRNxmlPUo+DAb+zXFA5SiT2OSidUKnVtx7MOHClnBxzHd+ym2FGPlLatHgIyW+ypXFpJTDEblJ1a - 0j3N77WrJA2IaWb+HNWMCYexFUSXLhwEChJQgGOb0p1SUBBgyBi/YTgxHm1MmoqQMS4/k7wmGAVZQBjs - cB5TR+fO666vSWHdAqWinrPuX8u8tu+bMQqjorFtQM7E60W8vpUg5VDash71YH/weQW8QN3bknF/XWGn - kwaDFvoCowzp01bk/3MLjOM9cr6lqpdqrc/T+oZweJJOUBYUG5JjVCu9jDb7RUH6ajJN6zXFNCzXFJM4 - v5PKaInIkcO6qEbMXqcZ0ku6nfPoimJYeUQ9PLI98g8ge3LncsY+Mhk5KgMJgDwn0HDUCgnrhWi/R153 - q8uCL9uW7WPbgR4vwkk9/AZgkFpYxpuwnvKYzwpUbD+vVe9d759T9qeMLTCemag+x4TjWtJasZRqVPin - hl5WaGPM9ELfwzI3RhUgGg56IHqPcbHygSQp5lrF9LUgc/mqIqNRRoohCogDxoJhLROBI/2OUpBKJZFt - AMnODwxCIhj6igKgJk71yJiIOjjRDcJzUi6uqPv37ev6VAw1OoH7dc77bHDYjpjWbyrFxGcABUFqwXvQ - GMOjcLDw+Wrb1/PdHt7HvoZdUc6raETjOZ6+oVgpRTjqWo90SzWlp2u6fUj8R6qXqR4npdykFs1plGN7 - j51irrFVo9JLm9SoyDW9PAqICsKRZTlrxZLIZftV5m3vgUqc1LIV5JpeNJnTYzQQvdMzsSoFk81y3a7J - z19oVKw87yTlMazncYHLxx6QxqQGzucXwECNSgICJ/I842tbHY/C/TyW7a5tGubzKEZVKELB34wRMaVC - kp5H1ONAsQJIklZQjtn3ABBL3HRPq2pRQQqS+RVLu6cCcq8ewHEU5IAx/2M3gMxIegkoBQg7xACOA8iC - 5cAxlKPAqEhTLFDEwDFGOZK3A8s+cbUmTSXpiWUSc41mTXw1mNbyGF1/iQ0NISDnNdZ9LAtIIHlubRdA - o4i0z2m/E4DCiDlFTfnMbLfQ6S8qdukqFIEkP6U9S1mVw5IWj+HXKNuUBhJSy0wxM7UY06CaWlg+cFzA - UD0eSzEBpdPKrQeJOWUsBQEKjSlKIiiVYrZBLc9RMdvpppiVXgqImMIzrnXCUDl87WzBSFpRHVCBrQx7 - 4pFwAaij1tvrsS6zPo8XCNbnfoLXdFnQAIltYNtRChTDK+Qxp5hXPjvws43n/QuQVaGs10n5qlpknIb0 - tloJIPqOD70BJN4jcKAcmFMVJEDoPVSTaUzjPx77rdROL51iUJJd0Rz1aO8R5QCQhuQAojktMBYUeA17 - HkBxZ0xLSdp/mFYCSJvSTivLcO7JZ+Icb5dz23MY+zyGgDBZZ/2+b0U959yXxzc0WVfvXe+fVMY2U83Q - LQUSvIe9Dz4znwFgeW48xidvQPAeOVVfqlGKEdUov7Fi9jm6aknH1BQjIA1FwAgoKXE1qwKiekRBhCR/ - KJTAqCaAI7DMSw1bPdqcCgmABJRWkA2IPRBTTdJLTOlVNVbgPyqlXPsNASNwAApHH5BUsDzSS0CI6YyK - ZDLN9d5mQpwU1aKWd9Rkr9fw/nosMG1AJjB5fFSG57CNfF76IQICLJzY4zOy3Tw2MCSN+Nqs43ankzTA - 5vmVjlaPO3M6VCSA5ORcoAkgx4dc4EA5WkUuCnIHSFSkFWQtA8glxUzvMVPMBsS0EjBGz2OpRpQDY5oU - 08phI8yqRTh2v2CDUSllg8EE17gntgFJKskRuya1JpTJ3RPsJD8W8/FrRP5zm+etdLD8QtY/M+v3Y9lG - Pi9nsQED/wEs7As+E7Cmp4HXeHopRjwIcNgAi9coQBYcQtFXpjPGcwSSlLQVBUe6pg0IYLR6TFN6VY4N - yDaowHHKW+J4ET3I/eWG9+XtY6f7d6v9gFGeI8a0UsyOpJacbm/F6PMhHI1dreA3nlfrUA2WVY+oxi5J - j4IkxdQRvyavVMIJPhO6r8Jak30meq6r9TGPmVTSAX2JeIUso0aZcJ7PNnEQUO7aeufcC5+fz8Ljk1pi - QImUsl4qGNVARaYRFZAGBTjWuNUDxRCOAAIQwiEglLi3zbE+/3JAucCx1ePiQTSnlrgA0qWugOhHAkjM - aSLAlIIUHJrSNeo7DiClHrty2aklarFgWGa0qpcau+9QqWUtA0RBcuCIz4h6MKa0LQU5UFjFROY7chTz - mAPGnngf4zKTq39wsoWMbeCz8HlREctbDgTum2BYneT1+m87pmrMuKrGSi3be3TVksrlXjlsjnVqaVCA - BBURFErcgHEBhHGoh6mlY4NySTOdahqMUcXY61BBqiG2/QeQCEXAMLUsQAqIlLGnitmQMBYclV4CR3xG - DGXBscYKvEFB8Yhq7CO/QdAwcjtHeYExlCSewaP/E2uCuY/JVVF4T7aRz4EXoeRlBGzev8BYIFQAyIoo - yDMuXqNUY8GSlGJaSbeUKMXY/kO/cfUdho0xY185RoqpSkYVGT2QUpGr96j0ckkzUZFWkpFmtnJMSAKH - qeamion/iAcJIBjSXHMhFDOtmGaSWlpByoOgHju1sAwgpQo7nUQdcpuJLjgGILVujQVATbygZH3gWIpQ - ShFoAkYmkeUGJYrCJMc7fFy9RtLeZ9dnxKQzsm28blSDsnVFPc+yFnBSyrZqkF42GAsExjKipR7xHgEl - 33NpJemKxdSigjQUSzX28gFmKMhtehEQFKQB0agCx1VFpnpcjWpUpAAJHLnuVP8R35GGWIGCgqzlNqUA - kig4aoz/EJCkE+NauQQOqpV4D25n4pNGankrQk/+ThOAshUhsp/UIRRMJI/16O/JjZqwXIBUKlmeZL03 - sLMtPkY4UIoY0VYS13efYxrTKEcFsOA9LhVLq0inlygIy60g26BuIGZ6aUDapAaSBuUKyDybSyxAHk0x - hqlmNMou6QVzSvVCz6MqmG6OqSIFSaWVL7nAweQXHOvoTEpZUJzlXbFsMJJeurHFRLVS6DfiL4Qh67d6 - ANABI36hI+sEwmCiuc/3Fpy8j6WrBrS7oXke6vG0Y0pVjwJkK8cExWqlTamACIkKooroP266qDu1HFDW - ukcVhHFA4XLD4XhvVK9wpHo5gKggAWSUtrsplgqG3kcumImCBIyCZPsNACGtqB4znQSK9iClGAXEUgvV - o1Sh/UWnj5jDvt3rAkI6mcKR9ZrLp2V5j7n/kw6Ml8esZYBoj+Hp+Y+qx+SxftEpqnEdUY6dZlCOnVo0 - pq0igaMBiTlleVYuM67qgQfZPmQpSODYMdQjy/Eej3qQA4hgTP8RD0KcMlflOJAcOFK9BIwAYpBeLG/1 - GuZ2oBAIoQASFYMJMq0wBoKklEDS6gEIWU6Z6ZHPZGfiAUUl2a3vPaGlDLWcVBFAUh2pEnZAAwcAoAwJ - 7/c1ohwAkbI2j+tUw1jqcQAREkcNqsrRYEwPUlAcFZlw7PTyBEAYZ4q5KshOLxcFmerRkKAeBUill0ox - 3WLv3kdOxk1IUsl4EZC9j90cq7IWrxG/oVIAQMWamIJl307K0FPkh1Sc+Ex+FCK3Y1iFwMe6LBBOZB6H - euxrMiptPK1eyzQlEKzPcmCIkrTPEC4elzSSCBSkFGGJkpRJvfMdgeOqHqaWLmuvacVzMI8AUilmn8k1 - tnIYSTeqx4oBRzxIgzG9x0VBkl4CR3oeSS/C0SlmX/e5Iv6jG2SWtFGP0QAbMFxAWZFJimoEkKhFJjgm - sSEQhG0iV0wAhCKKwITn3IivVZNfE/8x6z1QKH52cn/1UeW4A8MIIHmv/LftbQjINKeAcfUfXm9K99RT - +/EgSSu737EAuR0fB2QGXqQVJHCkerlCMjzIMam31YuxqxhL24CSFntVLls1DiS7kmkf0t8FARig0Kjq - QUoxNKIFRVJHQ9K+wuVMaiZddTDNsJwjWdMYdRAoATFcFwgy+SoIz68J3oBcUsxazuRPBQloDcOuWsqg - bv9RY9bdmlPBmKBcK5dEAbEj6jH9x4JjAAIMc3mqxxWScdLukmJQEI3qVJJOM1tBNhynxH1BlgVjRTfJ - xjWmGNRlTMuDVHrp8ysFAWDstMI6Jga1KEjKKO4mV8GRNFIQbDgymlK2YgxVKEDWyP0CIBATGCa6lGGt - 57G8X68XqNx/YgPia/JerO8UIxxJMWf9VpCrSZ1p5rbFPptk25QKyQbjoh4Xg5rUcqlmNhgZb7uptwoi - IPEhSS0qyAEk6aUgWV7EDupJNRuOjAKSi4Nm9eJ4rVy6z1FwbAVRRQRDdWD5cThQDn2AaSSeoMAgPewU - EkVJuiHOZK+R+zW7guF9edxSDMcNCCPvwzZ4+6oYG5ANi/4jzTIh0XcAxIzbDmpDUn7koh4DkK0alroN - SEMiIEc5Dhx7+QDS0XAMQE4VM9rrMau5Yj39D0vc/d2QpRwAIRSWt4zHf5haTC9LKWZaUUlMK0ASWGYa - uWlcjXSS5SiI0DC5AWODAwQDHNbxfkBSwKx1TOoBY8MRNdjGs14zKS9wqBaBwced2zu9sGx60ZRmTKVy - rxw3cBwV0bAGkhoPIA1KQQEcQLJVZHqPLAvIVJFriok5DSDE9iDE9TxMG9XtRUpBZpMsDbIDRhnUrmC6 - tA0oAeKmhb5Cc6qKVBrZE3KJoQoTjproPTqhwsDIhNf96z5eRwijBoHB5XqNHQeQtT7v/3H12ITpZS+P - uAWjPUcUowG57X9YyUz1aAUJINuDVIp5pIo5Mb9+qYIASgMS1RAOljWrd1XMMqi71C31OOkl/sMxHuQK - CFAEkD5zKygBJOpRASR4jlKQ2/4Gy1EOAOF2QbIik90gAEhN4po8bifiP1gvQE50rd/g5LUFhMnnyE+5 - 6msGlrF+hYAIVMX2GlGUpBrTytV/3ELiNSBTQQQktxuMq3p0CMhNJ5XY6aX9yG2rfcVRjgkGBnVeF3Ip - cxNRjwXH9h/AQbRB3V3USjHxIBVCstPN9B9HQQqO7UO2cgSQdEYDR/xGvIeGlPSx08WG4ACxj/o8pu8n - xWTCtypsAASE1xWAUop1H88XCEHweYGVHsiGYcORFLNvr8cXIKSXHYGjQZn+o+G4DXshUzmSWrJ860N2 - BXNRjtlNvaYaFeQKScCYgFwbZaaY8h4LEsA4kMSommI0qJa4aZZ1L8T+B2BwW98Rc3o918JkBZYc1aUi - a/ICCIqSSiSxl9f9NZlMaqWarRh7YgkByaRHEQieW+q0Riff5wiXjyUAIu9NOuMPfoRhqsaHPgpIQ5GS - 9grIrXr09aekmAbkmmYOGCfNbP+helxSi+EVZTPFTBUJJAHEFJMeyK5i0gPBd6QPAiA5UYf3YLmrlz4X - U/5D9Vgjt+2BJM2k5I1p3dVMwZLrOJJaUtq6HCiSWk6sySwoNgzCooJUCipAWk2ERpCEoFPM/meFA8QG - pB6X9FLKspbn+wWMFQWEFwQBSsDxdgOiDxEUu6eBJNGe5HFAAscBRDgukGwvslPLFZKpINucbkACSRTk - 6j8aklHm5lrUY1CPagCHVUx6IGmSpYqZoOg/GpD9m+SXyJXirSLAEdPK5AFDg4JqMM5JT3qplLLhcHJr - 3QUKVSJjXntVRuu1cv9IJePxASHrBaTXqxzxG4FpA3LUIya1l+NBnpxqPrAACSQDkEdSzRWMGNVWElLN - YypCaFY7vQhI0sqM4UFQjEt62R4k6hH/YXqZHiRVTFILY0Fh7GqmvAjpZlc0Xc0sOGr05FzOjXh0Z2Qi - O8UcIDYE14ganMetcIKZTEbWAR6vzevkvg8LFIQgjeA12Aa2KTBFPWJEh3oAzIZjAiIQHfOrDlER1SPL - N/5jL+tBLspRkHQfJIDcphlb7Y9cE7JiQqJRbUACyTap9EH4gbiVarbvQElS5vY356oHcgNIYnwHBi9S - gOyu6k01Ex+SNAMoGeNHAoYmdZS8TPyGg4lsFQkYB5I1qQKS21GJivU4wTuTfdTDtMJksy73s97tyLr8 - RZgppsZajv8QjAKlKhmVY0JC2E015Zhq4kcKjILjsRRj+JWHq3o0IEktnWICRi+vGGnmFozjQVQOm2Ms - X71HzuDaIAsYX7ihyXKlme1FKtWgHqoGYDgeSFSQfO9EJUnoTeb5F1QkJa9AOOkNS6AQJCfax/H8ei38 - zHpc7r9XjDyn1wmpQAlGp5h99narSsDoE3VRiyskppeAETW5gCEcN5DcphjNaatHfAhQdBVjepkmNWnm - cUBILbOKKfWgQRYFEZBujnl6H/WIgqgoXcnsq9j1IduDqB7n3EzBgYLgPzK2gqQfEjhymWF7kFzHwWSZ - HgoIVWVNqpCYkpzoPIbzKeu1tr95DI4TC6a5rKrxegVIKcaApZa7QXbGoSIqi7AEDkciRhUg0ijDl/SJ - uk4xCxA9xx0o9j2iHr08UgxAbDg6vVDF0Aux3L1REEtcfEeaZHZRvQZ1mlR9CKDkijIrGKJSS8GR6kVI - jEuqKVMac4rCOBEJlGP3QsaRn6N/p5ACZZSnpRJXOIwA4jUiSTGsN434mKwLPIw8RzCzroG4QgIgKkgD - 0ulEMK6A5L4A0ilmtNovKWb/YN2GonzHAMQ0IxAdnskdVcxRjmuJe1vmbgXZZ3JLRXaJW4DkMsOjJkdR - kloq3QDIUo3AART5YnbSTADRkwBBmdZtXIVFNQGCTIYt9x5LRTYgqgiT12qyU8xWlIRpJ6ES1fPX7aMU - A64DXAGUfgqqxig0AkGwTiiIk3pQjwKkKxij08yExbZ7/tHSiIoEjijIVTUKkplejv8AjFYPok3qPSCJ - W5N6ylyvAfn68iFREKqX/CBdxVGTVDQaVQxqq8g2qns5qpIr3DWwKgwpp3wKzTSMbF0KkHQDEKiKMDQo - rShMWE38BsVJbhhyMi8AMdHxMAXbet08VlMbIAoagWHcz2Ob6jm1LpE0ExU5YOyUUnG7vG8LB9AEIECJ - WU1aSck7IWkF2YCYXkpF2odU3PkPmmSeqNtwrBCMhiRp5taoHgXRoJ40sxVE1ajlrR7xJNODpIohSllW - +nFZtREcQQksnvXtqDJ4G9cOIdlKUhMtKJ7NTTlcwKzJbPXYEKz1qBP3RQ2WQghCjQsQgCpIvC8KUrDW - cwYgJ70MRRnGtVLMXg4I+BBLXktgzarpxTO8pJndMBse5KjHjAsgtxVMFATl6BRzC8mMCUjgCCD7RJ0+ - xPMvlrhAUsAUIIGDdUz8gWKpSBSmT+aVuuz79CilJhsUlITbpJ6kp/iWSj2AUh7F8zWWwAHGyfY2cAiN - ylFwoBR7uXzMBqrW7/sLCAHxeQVCOq9sB+tv4ejbgaSUZI8XQPbtRAwr0My004B0qVtKUoBsSEo5rlVM - Ypa4AUVAAsX4XRAAIb3cAXILx0gxJ61sc0pERTgfk98H7TSTi4WsaIQlYPTZ3glJQxGvwggMrSQ21fLd - GeCwNW+/5FrhWP6iLPsio3WEs8xEsjzHLK8Us4EKJBOOwNCK47r0QFC0CQgBJI4n3exRKE7aKQXRtPao - Uc1oTyQNM0FpH9Jm9R6QexWZFUwrCOrRgFx9SAxqQDHFjEsOTS1lUvEiO820kuxUs1UkcPSP1Xp/Q2JL - fpbC8SRHTRY0QqGhVUkq5ZSRTfcVWASFI1owAkkmPaC0mQUQFSVA5dICYFFBVAvGAmqDEwA+6rxfAInS - TFACSbxIKQmQ7FSjmnSaQVVUlKSbBkRIknLu0sydF9lKcqcgW0UKkFQwV0C6kgkYMadXQG5MKgpSPmT3 - Qo6SrLEm3lRTAEQdWJ9UNLzKikCTZdYLTIMSzxIVWt5kQZKyGBUJKFY5ExirnUBiOdzeJObWkpgxPY8C - Zi1P5SloSi1MNfZO2vy63vesdcBRqgMYc1lQgGSHoGwFCSg7CpYGpL1In5fpkTQDFMOLAMWA466SwaAe - FbGKGQpSASBWL4mrQY16bAUZlxsWLKlqJixCYLqJukRhDD2IQM2UIyAa2Kzb53J2qknc/sBMgGGkmcbR - bFs+YGTiA8j2I6rHASR9FAHhMaYeIgD18899G5B7BbkNAZm+ZJrWREGz1qMkbVqnkphmplEVkAHJBgQY - roBEPUgtM8W0QdWkCogKYqgeDcoBpKCoSiagFCwFSExr1MP/cMl1qjWu+wNMg0IIxkk1y48EiK50hEUz - G28SL5LUE29y0s5SlXiSXEOSSd9pp470eBQnumEh/fC4BM9rcJ5eaYztQMm4P/el4aZyHQUpAK5gBAiM - 6k4zO9VMMEgtR1HWciCZndWpJJa8G5CTXgDkxosc/6Fi3HdQG5CGIz5kLZcHAQ59yATl9EGuZe455b+i - YYjHYNlUVI/fKpJ1tOjz3y8oRlSirx+ZgOBBNLEEj+G+SjlAQoqp1NMqAjRMZilJAZGWfKnKUZYAEyis - fKIetvYLgAVOwbEAAHyqOLad9wEwVIXn8X68ZpQmZ4qTWiYgDc4lzWwVOYEnMeWcNENcQZmQxI9cTeoB - pJRDD9KQXFPLIwqyjenVoDYYGeNBGAsQVCSqkV82PJBsANh5BcgBKFebAU6gSToSpKhH2vOtGirHVpRS - lQXOXo6axMAe9ZiQLGhUEYAQgMBBN3b2T1IKR1USgSqpBEiAg/fiqn7/2YHtSFeX5+fnqnj8TDvGY6Ac - BTlqskEBjlKUGNYnqYgmNWCgIrPcjQdpQICjfcjVoHZ6YXwsvTQgxu0Ju2FSneBSBlNNwdGAcJ9+RVML - KI4CYzoqQAoCwFgglKqslLNhuAcDkOJVpi8xzWSZCufZG5CtHsCxFWRCIEAF0VqfdOFJwU8u2Nhefpfd - Py3kM6JU9drrsbwXywcQIVnjVI6GJHGFI2MqmK5o9B89mmJS6gaOnJeJijymIMOcCsiBREA6poqcsUCZ - 1UsAuZrUY05XqqjlDQCArMkvQGp9rhsRqhr3cwNIzt9EQWJETTOAY/pRRSYoeWzSj+aVEUACx3NqtAS+ - QpFy1DQDIIHI9UKU8z6kDt6Tbfc3yoCEXxriPZOOnlXvA2DxJenWzkrnxI2SBJI+EXinIkdBTDPTqN76 - kKuKFBzDf1x8yPAirRwjvWwFMVo9LHEbkKMgBUZNcqBgPOmlxmU8UZcNg/dFUWbFsw1tQWJ52820KMiG - 5kCxYKlxp6INCiAYlWZWHH+yjup0W/22HhPulWq70ik4ogLnMSjIWs/I67EtAMGvHAIIP4fJj+sCckwy - 5vhzNnApoQNJTOwB4xFIUJC53GbVqmYCIhwBI3GbZqImQJIeyOyD3ACyolMM4416GCfFxH8EkAZjABIF - MbxdE77VI+knRjSPiU85z0NtAGdDEkC6aZZ1+XH8oyAnvM0IKH1up5SkurADlKEiB5I98TbVTDUAUp3Z - 9dg8Ptem8NpsK38L4u+k+kvLwJ5toOv73FKja8ksLKl0jicpIDYkB5gY11aO9iF6kGt6IYDlCkiW26wC - xVSS9iATEGMCYts9lcy9/7i22Y+CxKgyxmOw844yrMkVhAkGj63lrR4F0gbB0LBmVFUahh6jNDwn61Zs - NWGyWK4JO421/PYIk81YcBQYUYosJ/2kAqKXEvVh0tkWzKnphV9a5rdS+Y9cKhru5715PxUEUKhsAghK - 0iEY3ar37PCubKxiBiQNSJREo9rL8SOBxJ5I/8qh6eaoBxWM402aufMfx3vcKogpJtEpplJLJl9YjiKs - se8LFGd57cwAEoUBEo3qGVcEgFQ17PjAMMMLo5mYvmItkfIYQAAlipJfbo6B7R+qKZVYcBQ4gLIhqsdt - 9eE12DZ/ipu/P+X/6/hRf/7tgfV+Dt4XQDq6Gxtork031aTa+NvIxrAO02qqGSoiHA0G40w1+UomPZGo - xu8/CnLrPxoO47aa2X2QCyQCgooYO8WoDv5WWd0mlayJT3pZalKKATheXBT/oeIIWBlV4ViRyde0ti8J - MNzu+/J4AImaCMeEpFQFSGZTbaePqETa81EWf3ZirQeOBRaQ8dpsL34DQPy3B/5tClioavi8fBbem9eI - j4kixd/kPFAM7FKR3b09cOxRNdGLXM1qYhrVqMf0IglVJIAM/7HHCyB7+RaSi0E9YHSKUUlmNXOTYohc - fsgYBUnpi1LcApTHmmYCSVLNvjxgj5n89FECwwTGuJ4xdl3gCFC9TCk8/3Fy9EkAxli3NZoEUJX3WGmQ - z8MvK/P3Y4DB/8X4nzGkHX/xgO3kfYDMVKaSVIW0gFFJOuVsX3JK4ahIPEqqG5tnDUdDEiiEBAWZX9O8 - Ns2EJSpi8KMyy5OUkswUswFBOe4giYI0IBrV37oUhMlFIUgXWykKAo4iAdlqUTtuPS4Q5f9oXTbVMAGV - mhYc7U26pyIM7HylvJ7DxO3XuQIVmALI7qWUogjJ7pFcgGFccGzl4H7g4L15fX9dWfXgH6f45ymWURX+ - VIjH8HgBATggiZLYtTXtXH0JcDBa4VwBSbqJesR/9DgBua1k+tzMpZIZanIguaSZ2U1FQbYPuYHksfTC - eBTkgHJUoptmQMHvqdZvqhYQKwqU6UnW5BZUs3lG2RsAaiyAGpZ6D15fyPbzWebxKoppp6Ba4ASQfdlA - KcP1MoKsz8k/7+c1AI/PwN9+oB78BSpgzD82ZD3VDamG7QVKXyt+x0qpW/tJOV5OMCqc4UtS2WxAtoK0 - FwkcWRaSgNJw7K9FWMksQPQhqWRSzQBFQwIciVtAgKIBmemF6Epmf3l7TnqWM3mBhNv+ZDe9A8HoH+Ft - SAoQwNpwAUPSR/yM4AgGr8e/LnDE+tckvC+PK0iOknApQTfijj/Bl9SyaWheg5JLC4BM8HgPylnTC+ox - AcGHoC6kmgCyXne9B69ZSrXUKR4nJTaQWFZHTTwZiJpcr3AjVJBbSCYotyY1SpKvZxYoM8WUenSqaeWY - ISC7klmgqBwTEMYGZHsQAbhVBCc492fHEkwooGT9evx5XlJQnhdlqIlWMUhX57607Xktjmb+CoxJYeL4 - g6MCdD1W5QlgURRvM3FlXIGjILlWP0dx6jmcKvjG2j5g9H9zSSmAQfAfuoDiP1/iUdhGnut76GcuvqdS - D9fSdhe3Us7pnbR51bDOdJPS98md1bsUM1TkKIhxvEeDcVWQrSKkmBv1uHqQVpFqlCWiCB7ZRwn2jgUO - xlYZnpNRden1eIlunqkIB5A1OlkcqUyYJSeNK9YBCo8RlAogW89vVcHMznFfkgBQ9Zioju8NkFQv9DtI - L6gHYPCv2/y58p/72B8os8p28P8xfC7g5nWAznTVSpLKSWPcVQ6gbCVZoJxUs8YJh+kGQGocpW+nGhRk - tt6f3Haf1UxK244oxz0gGVWOVo8DSKWJMdlEIGkVcT07uB4PJDsOHK7bzxeQ62vZns9rYgaBwT/1oR+B - J6CaYIIAhwkVMAE5EGx1KAg2QJnQeBXuz3sGYt6P1wQAQEAxAOPD/uZLzt+z8/7AA0QcFHnNmOuksWGQ - McEz7byj3iTGtSDZJfD0JKUkw7ySZmykqR6MqMksewNIeiLHqG6DqgcRlPv0AiBJL60eURDAuKpHWu5b - QUgpOep7HEZ1B/cBQpQiwBRUGxLTD7eFQhh4rTwvELLMY60maFABCBPGJDF6FJN2eO0D2toWJh5gAIN1 - QFHvs+7L+qSiWr+VA7XitVAoAMBvCMdHfuIP1n/4s8yfLAMPHVbgZFt57UASwxxPEoOMKW5fMi64LiWx - sdbVTaWabVhbSahqOs0ARacZFMQGWppmfRHR1aQGjkQDMrqoF0BILUIy00urRwFynfDAUYCwUzh69lEr - DKpIKcl6Drd5DYKJZL2gRFF8zU5F9dx1H4AwCUwcE8LRyyQBCSqCsnDEx5egTElPbhNREzhh3vfzvrwP - 28T74CmAkdcFAHwHgHzEJ/zgw195xksrAIWUAyR4EbbLfyXntQu+nd6iJDlnVBXOVpTZM4mJ1bjGtKZP - gh+5ppoyqxWCAiBAMeEgzeyuasGxS92tHipIwzF/XchYkFwUJCrSaQY4HI8HyQSyEzKha7l2elLFXH8L - CDtvvobAcH+Bwn0LBiEieCz3CRQTSGlpmvEIxpcACUbW18z7pAdTr7VfOzBmO9w+3oMJ5rUBDaWiclE9 - gAPl+Gt/52UPH/vMlxUkAoqBJc0AFtvH6+egEZL4Hs1y9VtKTXJ5ZJXDZV5zOUKnmz7JZxMtYRvek3kx - qwElXsQUM0vdmWamgrRZ1ajeNMsukNwa1AYlgJydGwgyETnyAYXlQJI/HhKSmiyeu8J1c+LqMeM+4jx3 - 3+9jUQlgoPwEDtILE8Q67vM5PN7XsTw25usBH89TOYCDtIUxRT2A42M+5WUFx19/1ssrWEZF8CM8zu0w - 1fC6VyWx25u+jE05DayeREi6spl+pNPMAeSiIijI/EfuNqkXQCrFbJNakGhWheM+zXSqMVLqJgJOA3Ib - daTmiJySzm0nRJicNJ7n2FAFNNYTEySWfQ+CicAjoBzCgYnlvYCBx6MITDyBsgAATS2ey31uG/drgHkt - JhtVIH2YVp72qS97ePqnvbzib/y9l9dtICHwQXgVtodUw+vXZ16fLZ4kF0jFuKYHk95LLpk83dcDif/Y - ncsFCo7yIvtczYKjAj9yFKTP8k4Pck0xu2GmB1lQHFC2igjGY6CoIicFLWiuKeYAoZJEFeqIWcsFBulm - 5/bk91YSJ4Qj1gnnsdxvBIC8/rnt++51AuTkEkwKYPDaBLdJF4DB5AMHALiO2wSPs78CHCgHbXU8DqkF - lVA5PuEfvuLhGc9+ZcXf+vRXHFBIM6Q7PAvAokJCmM+QpiBm+Jpu8m3C8iR14jBKonENJDGus+xNumkV - 0bC2F7HUbZMaQJYHKRW5epBOMfoQgNgQ7GhgNK7TqObakPEzmB7J7UGyLkrAzoiKpCphPZPpBAJHATJf - bwAwQWA0BEzYhITXFRRTCUcyADDxBJNGCgASVIR1PIb1LKMawGHPg9SC51A5PukzXvHwtz/rlQ/P/EcJ - lllHABEqgurwOqQoXpv3Uc1aSdK8ExJSTlrzS0n2ORxb9Jdmml6kSl4uByBWufuIggCHRtVmmQoSQK5l - binJDkBoaGbjbHZUby8a2goyJ890EGNKWZnlrA8otVMq5aSBRjipgOIkCwLrWAYYbwsD78ttgiNTEEwj - yjojisEEAQOTRZg+CIAAFoL7hIMJRgloguEt8B3CARTP+pz/9PD3PzfxD577Q3UbUDCtGFYg0TTzPmwD - 4ed0v1had4WT80NHSWa62ZcM2ES7+JCdYqIiExJN6vYgR0V2FXNUZHuRDUNBsqApeNZywFhqcvzHkwyq - fRCO8jri25CqHFGUXq8XMdWYbthRU0G87eSzLDACwMh9QMF9rue2oBAsc9SiCgCB3GNkSRnCwW28gsFk - UhGpHMDBZOs7SCd/d4EAEJ/x+T/08Fn/5FUnuA0kQPTRn/zSSjX4Fl6L19WTkMKAmM/APqj9UU06002u - SIuS7IuabgBRReJDthc5aSZne9uD0A8x1dxWMqSZ+3L3qMpFOeI9AkiUwwCOwDJTzIYjsVNDwTFBWSkH - 9ShQkoamyqgETirLTDrh+rls6gAIJp9xwkEIkeqBQgCHygAgLM9wPb7htpzVc6AOgCEQn/2Fr3r4x1/0 - ww/P/ZIffvjcf/bDdfvTFySf8pmvPJDYQOM1eX0hYbvYvjow9j4KJOm6RkVszedMsBcf2W09XmQb1pNm - NiCWutdytwExAKSX3++iIpcSt0b9CClGFbltt08PUiqSiT9jrWtQCogDSEPCcq9PVaMizGUVxdtAICTc - dvSxgMP9TIReA7UQBCCgmWVZLBSkA1v2pAcmFziYbCadVPLsL3hVwfAF/+JHTjz/K3604vO/9EcenvP8 - VxVEwIRpBRJA05OgTnoSPJHb7H7yVIDNtOq4Lkg8A4yCxLDyR0jzcgBVZKWbpSJXo6qKBBJSzOmLTNNa - KcZqJoY1MBgNx7XMnQaV0RSzPlRiT/yG5QoDEVgwq4HDsnct7xQkRKyvHbZeS0gmHAZQsF5lmQrDkcnO - 5yidKsERbCD7gsEyRzlBWrBawUtQnQAHygAYqAUwfPG/ejye92U/UqoCTEAylQRIeD8gAVrgZRvZ7ltI - SDXE8SPLtEZFTDN9/chVQXaaGQ2zpBfDvkgM60wxMakbkKEgSS8bDFXkUUCuKlKAMHENygohKYUIAA3M - VpSzLuD08gZqK1A/Twjz+rwnnqQet0JQ9BzAoedgMiYkKIgew1AxhMOUAhhMMt6CSWfyBeHLXvCfH77q - a3+s4l9+Xcc//cofLVUh3fC8W0hox6NUbAfbp5IANp+Lz4wnOaa1zt3kBN+1gZZW/OmLFCC7q7pDSDLq - SZaCjBQz/UcAycm6huTmrO4G4xaQXvb2u3o21wllEnMOoyZvT3gpQ8X2IXvy674LAAOQfV/fTwgMj+3g - yOM9OQoBBPVgpxN4D4EADvI/y6QXwEDyMaKmE1SDiSQ1MLEYzls4vuKFP/bw1f/mxx9e8I0//vB1L/qJ - hxf+ux+v+IYX/0St4/6pJCjPTDdUQyoJ22ZFRT8GVXTf6UcwrqSaVDTLixQg+UJXAMnZ3rs0s6LPz1z7 - IZa6F0COilwbZYJxAEFJFhxRk0Q1yw4wqMlOMXyYfKB5zema6BozqfcT7G2hmI9x4l23lg9QK9YyQBAo - hmAQGlYbYQDCEQoUTIKgkF4Eg+CIBg56HJSxlLBUKYCBn8BXfNFXtWIAATD822/+Lw8v+tbEi7/tJyv+ - /bf8l4IGgP751/zneh7Px7dQFqMkvIeeBOUizVkCA7eQWNm0H4mKWNEQ9EUulwToSXZFE+WIBzGttEkd - KnKpXhgDyUwxPXY1032Q216IHoTJWxEl6ckVGkIoVIR4D5SlFeXAtaIhmuD0CTbeS5USFuQZQDyxBhia - U+CYyoG8U6EwQTS/gIMS1h4HqoHX0Hh+6ZpoVEEwAOGbv+MnH771u3/y4Tu+96cevv17fuqyLCioimoi - JCgJ3Vfei/elhMYUm27YZj4Ln4l9EUjiR7xMwLIXBQkkqWjSE2kFsRfSkKRRVgoCHNucBpAucbM8zOpW - kjappplZ5lrJdA9kf+0hR3h9oDOZK5jsvVwgbAPKRJ90s8b0QrZKzKjnAsi+b98WJNSD90Q5OOI0pQDC - jkayZ9NLY8pkEADCBCH39jcsY0kJGFGOflTgX28wmPT/+J0/WRAAw/e85L8+fP9LEy95+U+fkfUAA0Q8 - D18yIUGdgMR0A6RsEwBb3fB5+Gx1gK19xXmbXE/yJdUf8bpWVSQVTV8rolGNSZ1GdZS6ADIhOR1VzWor - yKWCIb1cAInfeByQPaFRkJxjqUmtCXVMeohy9PLxJirGeh2fX69XYPQJvflephWCnYl6kFoABDAIVAP5 - JkgpKAdVCikFiWdyMKOeTwEOylMmEZOJcgAHKeQWipe+8qcfXjbiFT+UYJn7eMx3fX+UBbBIObwekNA/ - QaVUEj0J2wck9klMmXxm9pfpBjXx0gC9SK4ZMcXsf7fa/RDSC5AAiKD0qf/tQXYn9ZJiSjlMLfcmVTis - XObypYppGOZVY6zrEIbcjop4fsbbPt/XBATh8DVNKSgIO8+qBTDwHbbP2cnINnBYwiLngKHfsL/hORWU - g8oDODjiOfLxGcChOggGMPxMwWOFBMiADUXCvOJt8CSU0GwHzTiVhDTI9gM5n4vPWAfM2k9eKtDna0w3 - /PrRvvLsckHRPDcDHOOknapRMQ1qUksMavdBAkgb0izPBplgtIosQJIGzsTuo/zEnuxSjOEtWCa9mHaE - RIh6OeDleX2pAO9nvwNAyN2kFhpfxIRDIzpVw/4GYGhIgYPJ40gHDswmfgLVAI5btfjZBJB83w8m5agk - phuUxHTD9gAt20j6s+sK7Hw2IDGNsy8pgem2Aklf9EyHNb9HYk8kHVXPx3ST7K7MLUD0H0tBhveYKnKt - WEgxSS9XQBgPID15mWxurwkXjgskc/JNMYymHg1tgOGxdeTUa6R8JkwrAILvYAdqRG2AsYM1ovQ2KF/n - 1V+ebGOCSClMGIaUI5xKBYPJUc/EMsH/LcpxGzxXX4In4fWBBJ8DmGwDKQ4PhC8BkulL+FwYbz4rBwOf - n/3A/itQlnkFFK9As8OKH/HUfwNyPRdzVAQwFiSmGMDoFGPVYtx3UQOJ6tEqkq9e1gQLSCY2kwo4wLGh - 2bdVj1KQgiYA3T4vrxdfIxgEYOA3BMPmF1BQNhKkFNOJvQ3A4GgVDjqdHMXAYVph8r7+P8SQMqGkCNSD - if65hr6E19K48j6WwKQbtoXtAhIvXUT1+ByAAvgoI2nn4k/WfjrnbwqU59aZX8tf0kxUZDTJnmROh4pc - y9xpVIWjDSqKkVTTYDDuKkYIGJM6biP37xTCuoIKMKZq7NcAkBW9rk/zI7P4DMLzKppPgh1KkEYIoEAx - aFABhl1RvIZ+g6NYOOhzzFIW76HvYKJ/PgEk+BhTDe+jH6GkRkkwrmwjkGBeAduzwUDPZyVt8rlJp6gK - B0orSqqdpJ7nVspBTaIkmFWqGE/WDTiOB+leyKxiLspxALFymWkmcABGja//DtcUcwDZqpBxpJaa9HQI - WZ4lbu4LINOQAgZHC1Bw9LBzMHEcVQRSbC/DSwEJ1IIdjc/QhDIBnmz7x1+Us6/CYTmLepBaKGdVj59r - arkNUw0qwvtY/gKJZ4HZzqkkQGLKsbGGUgKKZbGqAixWPZhZymKqHaocUk68CCnGb9jdeJBKMVGOip1i - AsYCZCvH1X/ki9sBJD4EQIz91cukD72E6tApJPedVMTyBoSxYt/XgPQXo8jB7AyAwHDiLTiyUIl5fSiB - UqAY7GRNqF7D0/RIOoAwMZw3sRFmv0P10Hu84pHJ/rkEoAEI8E0vgvfxvA3biopQXdma5zOacqjEVBT2 - BwcL+0ZVIe0KC4pbsLzHC0pRaKRRyTQg8SEnxVzUw68/6EFSyaggxpM9yGmUAYfK0RNfk71CSLJuP3bd - DhAsM/J7IpzlVYleXB+OD8qRwpGjn9BTIMEEKcRLAAmgsKcxVcP+BhOhcgAHEwQgTBayj/zbJWUyX9OA - mGYony172Q6rGlQEmNl2YDfdcBBwQLAfODhQFA4UQCG9YswFBa9CCqbk5wAjBZF6MLE0zzrNAMgVkgPI - qWLuPQhQNBztQ+I9Usm0gtRkX1VCINLn4NQ1X77Ob34ECB7bPqSes5UkgLyo6n8+sHBQgbCT9BWkEAI4 - gIKjjZGjT68hHOx0z6voO1QPAEE9kHsqF45sALH38ZoEhOA1eW1Uive7TTN6EbafzzMBQUkEBCUREFIO - SgIcpBz9CfsQONi/9E1ox9s4CySetBupRkgOHIKx1aMA0aAmzZhahEQ4LoBk4m8n2zSzu4CrJBMcIdGr - qDSst2LhQ/LhOVqQV9XDqoQdxw4kUA+OONRjVikCgnpwhD6mIBzF9Cfoe3Bkm2Je0wpCqCD4HADxXA3b - A8AoHds+4dCHWNVoWIWCFAwUlMGkZfslMaxfXI004KAv4jkZASklOZDkjw9vARGSGrf/uE8vKshjgABE - QRJQjMAQQITkqMlRlDynHrdfB2jwIUDCUcAOINWwY1AUgDHl6EE0pZaxpprpP1QR/Id9DyaH6oUj2QpG - BfmF8CAYXzwIJlUPAhyAC8xsryYVpeRAILUAB4qB7zCdkEasZK4lby4P4NoRLiyy9X7tpF7V46iIPuSk - GBXE0/2PNcpS4k4wDiAC4IRn8jPZZ7kA2SBMQMbjojhLVRzX41USdgB+xJ4HsLCjOJoIS1vNKukGYDSr - 7HQVZUKCrHtCjiMZBWHiUBCO8tc0ILwW53N4fctcDSoAm1YAw3Si10AtCPwF+wF/IRhUeqg1vZC6buSd - 8x85uVbkYxccuYgovZCUusSdUT2VDD4kFw3NVjuQNBwBoxUk6nGnIAGgU8QBoMYoyAFiuWmiQdnwFCSC - tD3KWmeDzPMuNMg8S2sfRGUxDSHH06+YevAlKgkph3RzW+aaZuygMplM6muizOU1bLmjUgBJirMHAsBs - K3AAO5+Hz2Ua4fOSQoCCg4Z9w74ibefcTH7qKi33/qrmuRRxX6NqR/VUMneQbLNa6tFt9i5zNaaJW+9x - C0lVMUx2TTowjImvZaBYcJAP+SDeNubzXC61WYBETbpRRgAJaYcg77LzCHIywY7FxJmC2OHItSaWo5QQ - knl96TSqpIGpIj9fSFQPT9rxfqQ54ABcIL6FA/hts2s4AwYHYq4RyVXv+d1X2+ycsDtgoB7nAmZSDID0 - lWWPVjO7HwIcKkfCJpnKkdHSVkAKjtd/hxVvHwXpiU6qiFJMOPJbG2fdvh9oAkY+MK9RcDAWHFQ0AeS2 - 1T5P0LETVROvEiMFWQEh11Y/HKV6ExtmQKJh1YtQ7nrtB8by5wMIz7X/AYB4HqsW0h5pEDMK0KQTjDmt - dVIJZaon6rJv8Rh8b2b/nKe/LbJSSp2kW2DEcwQMT9alSaY5jXq0QX2k1B3+YyrINb1EQUwxqgZg/JLX - fzsBMX0IwABh36cMCkY/XlDygy5XNZkqkvMxExBO1hEcXYDCSH7mnAw7l0BRgARFIe1Y9XC0EigJEwQo - U0k8H2OqwViiJD8XSGZqIX2ZWvBBvDfbgV/SiLLNM6UAR9ropPF0R71oaF7l7o/yXpRjXw8iHLkGZF9N - toC4eI9qlCW1zC5qm9R0UwWj1YP0shtjpRoEcLzdwy9+vd+4AalJ3+llR0FRCjEUY0HiupNyeP5OK/W4 - AgUVCSBRkRXvc/1yFZDgS1ASdiTehEBRgASXT/5Gqv1mHJMgKEwKagIkSDyQ4AeAxAuFph8Bkv/WvgiP - FQ4UyRN0qBZeCCVjO1A4QAYOth3lAHgOgBjQF2f/rX16vuC9ATG11Ncw6zoQT/PnZyC4FiQXCCWddEoB - jgYjyhEwbhXkAFLqYe8DKO77HlGQqEcUBCD2JB9YBGDCUMudVgIUQPC8AJKeSIyqcFxvv+i04pFcjSuA - oCIAwpHnKX92uNeDUO0ACpNhyrE1DyQ0p/QkHOFCQroBEtONvRGU4TFFcT3ncCxp6a14HQgQ4n+AA0AB - lu0iJbLdbD+fw9P6Oa+S/x9OWvHnNPc3/+tiofw0RNSjLzecp/oBpCqXUo4rIFYtgaLb67epJSWuqcWW - +ihvj3rMFFNABIoz8QsUoSggCpx9fz3mevs2tQSYeBqbb7UOSDYgqgmQYOAog6cfMdjpKAmwkN81sEBC - paMvmWWwp//nhUNM8mzBE0CgqhgoBmE5i+HlNTSlwAGQnmdhWyxjNaSnr7E+K5+dfajvOMqxfz+kLjl8 - B/7+ff5mSK4k6wuFAsjFb6AaAFKQXC8UCiSqxzanBcfsnArITC1RDZZJL0kxNdGoRHxE4BjpZUx2jQsC - /YjwXMYNDcs8HiiErhQEVVnryMuqCDsUBTFUEtMNkKAipBtGjlhAIe0wSUyW6QZIVBIgISVoXPUlgoKi - AAJAoBZAw3oCoF64DKlphdcTDqop0hxwkFowpWwn2wzomtKqENfnzndj9he6VQ++7b8vNbRy8VLDeSVZ - N8dML21GgQP1OCpCerkDJJAAR4CwrN3qceCYEQXZgExFUBVcXhAM3+FjCyDW39zHct2u19uvW3AEMoKj - CuUQED3JvMKMncyRyBGJcTXlIOMcrQSQTCXBvFoGqyRUGUwsRz/mUkiYfEBBIUghQCIYrBMOnoOn4TXw - OHgdQLRioZz1QmV9B59hwsH+iO/I75jd/rBMrmh/+vEfXiCU9CIcMadVvSwFiTHdoXIM39HpReUgrUz1 - oNex1eOSXjq1ZHw7FcTJjMuOguRrg97OCABRF+EwJiRZVmF2ain1IPbFR7Uu15IIiqaVnexFRUCiaSXN - cLTqSSyDba5N44onwUgCiUpCMw1FYPLtugoLJpRgPSCRnoADQ2ojbKYV3htg2TYgRvmAm8+hF+Pzz9Ri - I2ymF79Z1wa1u6YBpH+vPf5jKMhsq7/Rbec03sPO6VU5Asg9HIwBgygFEYaaaFVhBbLI+kz0zWMYuV3L - jntdjf5LZkCIL4makGICya5u1rKeBDAAhCDtAAlHpd7EykYlYZIAhSOZCofJExImUzWhJAUSzSvKACia - WEARGnocKgdwmFYwwygHikU5C6QoG9tFSsRHATjK6MGQAy7NMFRD7+H3YfIN/08s75HeB1918PswwEHf - 4/YrDiiH1UsAAQobY8eYlnJ0vyPLqV4OGAWHYPSo//jFv+xtoyBMqCBcFGODIixZ7995tKKoPqVEKwoI - Valub5M6xlQ9qWpQEBtp5G97JASQAAhqwpEKJARKgrzPCocjGyWx+2rKQUmAhOYWQcoBFL+KSYUiNNzH - Y6xWeL7nV+x1zBY6cLCNQM1nAHagZxno+bzne7l+eXtfnBz1wJz2j/xf08sj6lGGFDC4amyqRyqXJ5vS - WwW5KW8LEFNLxmNSa5IZtyIw+fdg7HTDYwwA2c9hRwDEBZr9moKTxwBI4KhxA8LIDmWZI5EAFpSESUDC - hcTKhs4roKAogIKieOIPJfFaEyGxoeZJvgkK44Rjeg57HSgV7wWgmlLgQPmAGzhMndwWGszp9B2lHhsQ - fzzm2hhLegkcdksfSS1Ask0paeWoh2VtpZhZtTQgglHLR0EajBpRkDnpj0EQWG7Wl+JkwgNEFAigWokC - FxCUctT6PK/W7dSTNJNOq6UvwTKQ2EQj1QAJajJLYGHRxKImKMlUE9KDJ/uYeFKHlwsICumHZeGYyoEh - BT4US+XAlHLS8XrybafS9Rn5TKzns/vdl1Qt8/u4++uWp6zVd/j1ypjSwOFZ2g3GgOPiO1CPAwcKYt9D - BZnGdENxUZCVYhYYibeJB5lpIzDkJ5RyO0riBM/J97F53FCdA1LUJwqyjOtIOQVIHW1JM+VLhmHlqFRF - gIQjFUAAhSOXCdK8oiSAgppgZC2FhQTvgJp46QCpY14ygD9BWaxWMLg8VuWwEcb7oGBASqUlHPqOUsj1 - +bJvsk9QD01pf+8lf7U6fUenFS8Euv1q5dV3NCDXcrZTDKXtPCEX9TiqcSAxpRDbgyw4oiAAssFwQvsD - 5h8TamJXCIKTLww8Dph8nUSnprymlZI7sE0sMXM3gAiJwUQg17c9ElMOoBCqyUw5tujtlzDxKAmQmHJQ - DYAxraA2Ew4v8FE1ABZ/JBwohwdP9gPGNH9q1D2PBccypfllIc678ON1UY7umv7pDcdsqe+ytryHgFix - xJQ2JFGPNqbzZFxXL/cqYuWS5TapR0EahjPW5KZM64lPycZyRu9j7MfxGhV7h/lacyceYJaqREmSanI0 - Xk/uzQqHyREUJsww7RB6E8ykvgQ1QUmAhDY5IFjh0FgDGG6TWjC4ttBntcL7sg3AIdCmlezHvY/4AtQp - a59daSWB78hJuVKPc7a2U8tRjuM7UI8GRDimetwC0qAIxpPgCBTxHA2K6eUoyDnCGS8fNn9/IQBAwYfP - simo4eC1vO1yxnUfcMz3Agy8Cab1pBuvSKMMTsoBGEFhYgCEkpKUY0MNWCyFURQgQUkABN+AogAIimAZ - LCTAYZ9D32Gvw2rFtAIcpD22JZ6D7e/GIZ8X1ciXsilr88uGMaU532LVUsa0ytkPOSfkLGn1HaUeqMZO - L7fGNGCQUkwvVxWZptRoOO6NaYFx1CM+pDqpHtlnosfRMKMAeVQ5jEDFskCYblgXOPQyMbYFCJ7kpJz0 - RYiTegYoKInnbhhJPxzZwmLqmWlnnscREk/yUd1MOPAqpCXUA7h4DQAEDtUscGzl2GAQ7Jt5ltbUoik9 - 51t2agGMfHP/QxYYlrN/KApyqVpu4bDfARAbjq0c7TsARNVoSIDjgDIAOZAUHKjHNqm3QDiZDcJ9BJT8 - gH1u5wftc7+QxJ/07bx2ouFQuVh/YEFRCpgoSUGy1MUSmElCTZg0IFFRVBV8AmqiR8E/2C/Bj3iRNCqC - IWUEGNKP51h4vL6D18UsZztSnrPdfiY+ezxHztRasVjS+gMx+o4ChPRScOQ3UG9L2uM9qnqhalmALDiy - nL5HA5KeR5QjvY82po+klqMgqsgtIFGP13rdtwYQ04CVjMbTyQ8MWe50M2+TdvK4/O36hGU+h9dvVZmQ - JO1EsvEl25tsVUnaibpME6vcqyr6EyAhWAYWfQmKgDLQmgcEeyQqh80w+h0YUyDjdXh9IRVqPgOfjc+s - Ie1Wepe0nVq276BT+tb5eSnO0l4rl+k9AkcrSNTjPr1EPQKGMXsfPxMciQbjbQoMISkFORO4j4a5Ljth - wtIguI4d1Pf1430dbgOEEF5ivafroya7DB4RZcn1JkySqgIoqAlHt4GykHaEBkg0sUBCqkEhUBG8CHBg - SlEUvAdpSGOqKeV9VA4VkH1AOgkc+x8eqhkGHLlKDDD87TFTC9HGNN+1PT2Pox5XQDq1dM8jgGhKAaWN - aQB5rKRdUNSoagz12EAIiaDsMjeT3WA4sVsh9oSzUzxqet0EJHDV8laV3J7QdZXT61IyH1XZinJRk6Ui - BQrpRyO7vQmQqCqCgrqYhlAWfATpBhVBIYAE1SCtCAfKMi8bRD14TVNLYM42B4x98c85Q5trS4/noNdx - fmYb3+F1pqjH/L0xjalNsWtqEYyGZHqQBcYCZKpHAHkktRzFuKaWAuSSXgLHa73uW6IgTM4VDMaA0IB0 - BA6j7i8Y1uNr9H9s89w839dReWbFM1SlVGZXPMKyJT2RP2SOsnhJY0CpSVwjEyosjCoKPoJ0Y58EEACE - EA7SCwqD0gAUz7Xs5v3Z1qti8P+5AWMqx4FjXwCUtELQSs9VYlGPvpTwVjmOKS2/MX8MphXkQHJRjgbj - kmIOGIHjAHEDxozjQZzoGdcJBoYAw+RPxZhAZB0AGQ0YO/f+fQAygPJYJkFlCTAxtcJR41KTpBza997e - vZQFiLAYqAuwoCikHFQEADCsQEGqYaS6Ib0AD48BKJ7XyhHA9RpRDf4zN+dXcuo+/wVzf3WYlxD6g3S2 - 0z/oklYKEDxHpRZPyNkUs51O7JQy4IjnSKSCuVWPDcdRklaRxwCJD1lVzHUinwDFntiAsNYfSPxflBxV - rvf583XyeCHq+wJJYBGO3G5TG0hMPahKoLisu8CDylCG5vdLBIV0QylM+sBnAAZBuiFoqmFOVQ/g4LXZ - Fj+j6cQfnys4dnc0lcruc2y/cdspzSWEf6LAQDmiHrNjGlN6vMcApCsXAfGMbXdMG5YbD1IwCIapZSjJ - TWoxtoIIQSYnkwg4PbqTsi6PbyjyH/nCdpYPSI9Axrq1zDrel9tZNrYvKWiiMqUoB5p0eRueqEwrDsDs - snmnI7wJKkKqAQT6HYBBusGXaE5RD1ILwPEe6YqSUtLfaMV4xgYjqlE/Y3k5O3vrOby2Y7TTURArl9Mt - ve15JKaCtN/ocy0Z0++4V4+oxW00IK0cB5LXeYsAMuNMnoDUhD5h4p3sus0f+mUdO7PuM87r7LGW8zp5 - 7X6v3g6AmScNWd8pydCrlG8pmAISY61f6wqgBQsqQqrBtJJmKHkBg5GUQ3ue3gkVUODAczx/e4x5Hcf8 - W7H8H24qlY8+1Yq+o9voufiH8TatFByWtMecqhwxpO05oh5tSmfHNHHAeBSOW9UwOr00LEtBMkkDiDmZ - K86ErXWR2Q3GDlUkJV/+QKcjjz3PG1B4ux+zH1/bke0JKF1S5zb3BZpsX6qfwNHbmsfE9xBAgpLgUTCt - phmClrptdUpbUlLg4IvUfLlpppL2GYHjaUc1Zmrxwp+00T3HEvUwvRwwKrY5XapxVY8Jx6xYUI4rIEJx - UY+C5JpeDiB3oASKqSK7D8LO7qO4x56suu26/Rgmldtl3AoQVCRfCoqZ+7w1Jnd7n6/R67w/Y96TSWXM - +/fzxnbtMdsPOHlOQVb3re1e43ndtVxqspUEQChnKXuBw9IWePAevJ5wzIYXQYVSQWe0jGguNtZzFBzV - JfUrk3qOmxNx+g0hIb1sQAIE1UogSWqJ77gqCGklhvROPQoOFcSYpvTJynFSTHZs5/9MSnobiZ06aqI8 - whOZWGPDQa5eRi55+7kDHCHaE1iTJyC+T54/Xy/33YKVK8TdbkE46lGP9TNk+1jH/UCCceWcDYpBAAcp - h/QCPKQo3t8zsfqMSikbjNP82pVK4Jh/3xE4iPiN+b0WDOlOKeU7vIywS9vAIRjCscaqWvp0fpvSG+9R - YLwa7/GzgON4kEx4pFtgriAAButzX3a8k7onlglbk8AEdzz3PC5wZKLq8RuEBiKvkeV+Tq97Xl6/bud9 - 3bZ67N178toL0AreYz9nvQbGlVTCuRYCSDgpR4VDCmKf2PQ6YJhSdn9DryEYmNH2G6lU9B5Xz+E5lqSU - AgPVGKnF9BIwrp5DM9pgtPe4AHIHCeO9KW0wruZ0ANKNMie/QNhH3Lm9Ijue7uGaiD1xc8Jm1OTuCZsT - 5ePn8+a6y/r5fO5bt486+Zy5juVVgvpesyRlHbd5Dp6FaoZzLQDieRfSC9UOj8GQ4jeiGDGip0qplOJX - FPaFxvusbMyov+Exz694Cl9TSklrryMn4qYpbeVYgFQ7PZAEEH3HPSAHjgLkmlruFaRV5MAxAVnLp4oB - iIoNR9+euZyJSy/ASctyJp7Rx9TknUn7nDNBTh73s95wQnO/jwWQvDa3K3j8ftzdcoVfL0h309PuBvfz - eSl5URHAoLQlvdAYIwXx/HiO/Q8MpJNLSmlArv0NIq3zwNGjUNSI11hQNCDxHBMObwcMDall7axcHoPi - mlpQDG9f/ccNJBuKCco2qaaZkVIKDm4HGCZpKogT4yQWEN4GjIKjJ3tOpM+5ndR6zH6etyvqflvaz+7X - WuOBQCDW/SlJV+VR8Wk9rpTBY/k89DlQDC8HoDdC9cLn43H4jglGKpSpGrbM51+W5txKp5V9fmUtRzG2 - Kd3eI56Dcy2PqEbFvn18R1JLAzKM6UknhleKjbTy6uB4REF+0S99swCCSgSSpJKONoWJeIU6+p28PVkd - TP4aa6IDgpNZ45nozwwENfmkgH6cQOT+nO/gvjxnrd+PqdcpOLwoeECxJrlK02UyHWt53ce20UjjXA0n - 5DwxR++D1/21y3cUFAWHpWt/683yNXCkSolyoBYzrbx/lks9rqbUlDLVI0BYqcR7VHoZ7fTAkZRyUY4L - JJ1WhKSWHwHjDo4VgJF4U01qgDip5sAR/8HkRSEY2/ydSWfCatJ2qrg5+jO568jf5y5cvt4OKHmO96kK - A4SCZN+/leMWiMcjDS5SB+/FZ6VaoSmGOaWBxmflNWJEoxgHjGVCLWH7ZFt7jQYk6cSYJSyhagQQwJgt - 9FaMqRoC0pAw3lQtTwAksdTkAkbGWzAqtnIcQFKxWOam+VRglO9YVce6jyNO9TiTz8Sv9SwXJAXHhCVH - fqKV4rEJZuQxBUAtL3XgvhVn8tcoIHne/hISisG4Qhiy/MzEuJI830X5xKjI2tZSkvf8+oID9eC1+4vU - 8zzKnx9Vit+413MEEBRjqsfxG7tSqeUFSKnGrlJmBI5b1biaUSERiDsluQNjwHFSTCtHQ2I8AkgUxBLX - uPoQ0wrrkgoSNekFQ0xhTfy6nQnc6WDHmdw13t6+3pcLfQNH0gXLHPnc5jEXGNb93FcgFBScbv/kA0SW - c5vJJ3gdtoH1QEIlx3ZzX1TjCkfAsITNOZX2HVGOmVJOCbsrlfYbGlKrFQP/sX0HcGxIpu8QBtXjQFFg - tGokpTQkx3c4PgpHK0jDcQGElBIwCooCY93ey4EjVUfSzK5A1k6NekQtMslGJj2T6rjW7YnPOkGIkSyw - uI0irNsqw13w+DUGkg1HqUQUYp5E6+XAQWWS5/EXYB9X9/FaLNvTsNll6XqFo83obWfUmIb0pBTgWMox - AXkMioYDMFLWJpV01ZJYynFJKWssWFo5LoC8OjAwpQeONx2AlEmNglQ6OXAsYAoMS9tEyfL2GKaVUhBU - Yx/9lTruIAGATKoKIBBnwvf9rI8yBJACoNRDKHba2JMsGLcqkckPMNXLsJ+xRp7LfVWy3nRCoxpWKIRe - Y/Y3ACMROLrHcWdGFxwBYqqGFUsDAgyODUbguKjFisu6rRxTNQ4YT4SjFeMAUqllwsFyKUiqmARKknMs - gWOXtUuGk1ZiTivwH1s1TBkFBypQYOTsJ5NaaWNPvjB49Nf6uk8AAoP3J5I6TCOB4Rl1+1YxqnexYehl - /k0yZ1qBJ+rxtIJCf1EpZcfteZSZSjqd7BJ2KMeBpMC4dkUbEOFYSrFCIDqVqBQComo8UrUcQB4B48DR - gASIG+UYMZVjAIKC2ANJWhGQAmPFRT1QDJSj1MJRr7EBQRk2DBeV2DBk8o0JQb7xDgDCEIXI7SsUn7SX - n17LAYFu54q13GWqPYyoBPfxOqw76WQBUUFaufQ3uoSNcmwTunsbBcMxo++3U8lIJwXHvJa0VeRUKwVJ - N8IaCiNqcackJ6W0/xCQA8sdIKaWDckNHFdQAOSpuaLsalRtlqVqERCBERCVI5AM03kDyARDOBqSrQwF - xprwtcxRryoEiqy/TSF5XK7gQiHuOp6YzZFCcn3Gh9XjeD7rr6kk33BTPfQaM5UcOLZyXErYXa2oHoHB - 7mjAAIpregkcQBFA7HEktRh3YBwoAKEBuSjHUZBXk15uAJnKUfHabzJNKpE0k9QSOEwnwnGqlw1HvMYG - A0gKjk4jBUYtqxRJK1kOJJn4nEoPHLnm4rHUweQGCM6POPKdkygF65j8Uo2Cgz4GcKQDmud8XC3rMQKE - ZSth42uchd2KYRwgLpVKG9GohoBYxnZaaUCepBytHhc4DiQNhIpRqnHACBx3qjHgeFw1jKcGkPYeKW1R - CxUkgCzvISgrrilG5bC1PaqSoxKCkOVKFzW2sczyFQZVgnVOKqFKmEa4L/6iG1tEbm/jWYG/+PB6Ds9P - +Uoaye+QTkj0Gm1CJxidUoDkpJWdUhqM6TmmGTWdGNdeBwA8qh5bJa7qcfUcrr8HZIBxA4iQNCALjA1H - AKmqRQ+SLwOxTjgOKCuSVqxg2m8ISOBI9XEC9dBHFCxXxZiQmDYmJPEWSR8BJVdwEaaPBsRq5MPOaFWi - EY26fEwBEqW4RvuMbpPPmGkFMKxYAsb7HECiGPqMjAIRQ5oRKBwPDDMOEIwGcGwYBEQozvItHGt5q4Xx - agHZ8RRUw5K24ACSBYZmVVACxfAcwoBa7FGlEA7VIxDoKUwfWRcwAsuBokDYqqHhPBGlqHEDYvq4VQsb - XWU+l7fgdlLOR2zFmP0M2uV9e/qMA0Z5jaQUfcZVNZJSTCUntnLEaySdNCjcvkklE44aH1eOoxYTkgHL - AQQg1ngPQwDp248pyKhcMsaQPgpHpRf6HCOtbBhOetm3C5YCImC0eghEqo+oiRfkRCEKilKMrAMMj/yo - hKlkK0apg3AEkGtFEq/RgPjXXvfnURivitGn6FWNAqMA0YAaM6V0CdupRTgyMvn6jQsUBYLRajHhEAIN - 6N3tesxQkQ1GwVLLE4ibccNxl2LKf6zbmtGUt7O0TSStpBFWqWUtHyhKMRYUVaEECACJakyv0T5DfzFN - aKkFqWQrhzAUEAsClUCliILs60EfgQMgUBGBOkphdXKM6KxQ7n3GBMK0EtVon5G0IiD9D5P6jB5dfkRB - LorR40UpjAEGQNyOR0WIAcs9IFc4ftFrv8nD/w9gjh4eKTSCZgAAAABJRU5ErkJggg== - - \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.Designer.cs b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.Designer.cs new file mode 100644 index 00000000..aecd202b --- /dev/null +++ b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.Designer.cs @@ -0,0 +1,407 @@ +namespace DiztinGUIsh.window.dialog +{ + partial class BsnesTraceLogBinaryMonitorForm + { + /// + /// 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.components = new System.ComponentModel.Container(); + this.btnStart = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.lblQueueSize = new System.Windows.Forms.Label(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.btnFinish = new System.Windows.Forms.Button(); + this.lblTotalProcessed = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.lblStatus = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.lblNumberModified = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.lblModifiedXFlags = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.lblModifiedMFlags = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.lblModifiedDPs = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.lblModifiedDBs = new System.Windows.Forms.Label(); + this.label12 = new System.Windows.Forms.Label(); + this.lblModifiedFlags = new System.Windows.Forms.Label(); + this.label14 = new System.Windows.Forms.Label(); + this.label15 = new System.Windows.Forms.Label(); + this.label16 = new System.Windows.Forms.Label(); + this.label17 = new System.Windows.Forms.Label(); + this.lblResultStatus = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.pictureGreenSpinner = new System.Windows.Forms.PictureBox(); + this.button1 = new System.Windows.Forms.Button(); + this.cartesianChart1 = new LiveCharts.WinForms.CartesianChart(); + ((System.ComponentModel.ISupportInitialize)(this.pictureGreenSpinner)).BeginInit(); + this.SuspendLayout(); + // + // btnStart + // + this.btnStart.Location = new System.Drawing.Point(478, 12); + this.btnStart.Name = "btnStart"; + this.btnStart.Size = new System.Drawing.Size(64, 38); + this.btnStart.TabIndex = 0; + this.btnStart.Text = "Start Capture"; + this.btnStart.UseVisualStyleBackColor = true; + this.btnStart.Click += new System.EventHandler(this.btnStart_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(15, 44); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(60, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Queue size"; + // + // lblQueueSize + // + this.lblQueueSize.AutoSize = true; + this.lblQueueSize.Location = new System.Drawing.Point(149, 44); + this.lblQueueSize.Name = "lblQueueSize"; + this.lblQueueSize.Size = new System.Drawing.Size(13, 13); + this.lblQueueSize.TabIndex = 2; + this.lblQueueSize.Text = "--"; + // + // timer1 + // + this.timer1.Interval = 250; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // btnFinish + // + this.btnFinish.Enabled = false; + this.btnFinish.Location = new System.Drawing.Point(413, 12); + this.btnFinish.Name = "btnFinish"; + this.btnFinish.Size = new System.Drawing.Size(59, 38); + this.btnFinish.TabIndex = 3; + this.btnFinish.Text = "Stop Capture"; + this.btnFinish.UseVisualStyleBackColor = true; + this.btnFinish.Click += new System.EventHandler(this.btnFinish_Click); + // + // lblTotalProcessed + // + this.lblTotalProcessed.AutoSize = true; + this.lblTotalProcessed.Location = new System.Drawing.Point(428, 174); + this.lblTotalProcessed.Name = "lblTotalProcessed"; + this.lblTotalProcessed.Size = new System.Drawing.Size(13, 13); + this.lblTotalProcessed.TabIndex = 6; + this.lblTotalProcessed.Text = "--"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(303, 174); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(117, 13); + this.label4.TabIndex = 5; + this.label4.Text = "# Instructions Analyzed"; + // + // lblStatus + // + this.lblStatus.AutoSize = true; + this.lblStatus.Location = new System.Drawing.Point(149, 24); + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Size = new System.Drawing.Size(13, 13); + this.lblStatus.TabIndex = 8; + this.lblStatus.Text = "--"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(13, 24); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(37, 13); + this.label5.TabIndex = 7; + this.label5.Text = "Status"; + // + // lblNumberModified + // + this.lblNumberModified.AutoSize = true; + this.lblNumberModified.Location = new System.Drawing.Point(428, 199); + this.lblNumberModified.Name = "lblNumberModified"; + this.lblNumberModified.Size = new System.Drawing.Size(13, 13); + this.lblNumberModified.TabIndex = 10; + this.lblNumberModified.Text = "--"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(303, 199); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(114, 13); + this.label3.TabIndex = 9; + this.label3.Text = "# Instructions Modified"; + // + // lblModifiedXFlags + // + this.lblModifiedXFlags.AutoSize = true; + this.lblModifiedXFlags.Location = new System.Drawing.Point(146, 126); + this.lblModifiedXFlags.Name = "lblModifiedXFlags"; + this.lblModifiedXFlags.Size = new System.Drawing.Size(13, 13); + this.lblModifiedXFlags.TabIndex = 12; + this.lblModifiedXFlags.Text = "--"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(12, 126); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(42, 13); + this.label6.TabIndex = 11; + this.label6.Text = "X Flags"; + // + // lblModifiedMFlags + // + this.lblModifiedMFlags.AutoSize = true; + this.lblModifiedMFlags.Location = new System.Drawing.Point(146, 152); + this.lblModifiedMFlags.Name = "lblModifiedMFlags"; + this.lblModifiedMFlags.Size = new System.Drawing.Size(13, 13); + this.lblModifiedMFlags.TabIndex = 14; + this.lblModifiedMFlags.Text = "--"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(12, 152); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(44, 13); + this.label8.TabIndex = 13; + this.label8.Text = "M Flags"; + // + // lblModifiedDPs + // + this.lblModifiedDPs.AutoSize = true; + this.lblModifiedDPs.Location = new System.Drawing.Point(146, 176); + this.lblModifiedDPs.Name = "lblModifiedDPs"; + this.lblModifiedDPs.Size = new System.Drawing.Size(13, 13); + this.lblModifiedDPs.TabIndex = 16; + this.lblModifiedDPs.Text = "--"; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(12, 176); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(63, 13); + this.label10.TabIndex = 15; + this.label10.Text = "Direct Page"; + // + // lblModifiedDBs + // + this.lblModifiedDBs.AutoSize = true; + this.lblModifiedDBs.Location = new System.Drawing.Point(146, 201); + this.lblModifiedDBs.Name = "lblModifiedDBs"; + this.lblModifiedDBs.Size = new System.Drawing.Size(13, 13); + this.lblModifiedDBs.TabIndex = 18; + this.lblModifiedDBs.Text = "--"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(12, 201); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(58, 13); + this.label12.TabIndex = 17; + this.label12.Text = "Data Bank"; + // + // lblModifiedFlags + // + this.lblModifiedFlags.AutoSize = true; + this.lblModifiedFlags.Location = new System.Drawing.Point(146, 102); + this.lblModifiedFlags.Name = "lblModifiedFlags"; + this.lblModifiedFlags.Size = new System.Drawing.Size(13, 13); + this.lblModifiedFlags.TabIndex = 20; + this.lblModifiedFlags.Text = "--"; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(12, 102); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(127, 13); + this.label14.TabIndex = 19; + this.label14.Text = "# Instructions Uncovered"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label15.Location = new System.Drawing.Point(9, 81); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(194, 13); + this.label15.TabIndex = 21; + this.label15.Text = "Data Uncovered (#modifications)"; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label16.Location = new System.Drawing.Point(12, 7); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(116, 13); + this.label16.TabIndex = 22; + this.label16.Text = "BSNES Connection"; + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label17.Location = new System.Drawing.Point(300, 152); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(57, 13); + this.label17.TabIndex = 23; + this.label17.Text = "Summary"; + // + // lblResultStatus + // + this.lblResultStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.lblResultStatus.Location = new System.Drawing.Point(310, 102); + this.lblResultStatus.Name = "lblResultStatus"; + this.lblResultStatus.Size = new System.Drawing.Size(236, 37); + this.lblResultStatus.TabIndex = 26; + this.lblResultStatus.Text = "--jhgasdjhfgasjhdgfhjasdfgasdf kfhfjhf"; + this.lblResultStatus.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label7.Location = new System.Drawing.Point(306, 81); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(43, 13); + this.label7.TabIndex = 25; + this.label7.Text = "Result"; + // + // pictureGreenSpinner + // + this.pictureGreenSpinner.Image = global::DiztinGUIsh.Properties.Resources._1603231497loading_green; + this.pictureGreenSpinner.Location = new System.Drawing.Point(303, 12); + this.pictureGreenSpinner.Name = "pictureGreenSpinner"; + this.pictureGreenSpinner.Size = new System.Drawing.Size(46, 37); + this.pictureGreenSpinner.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.pictureGreenSpinner.TabIndex = 27; + this.pictureGreenSpinner.TabStop = false; + // + // button1 + // + this.button1.Enabled = false; + this.button1.Location = new System.Drawing.Point(355, 12); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(52, 38); + this.button1.TabIndex = 28; + this.button1.Text = "Help"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // cartesianChart1 + // + this.cartesianChart1.Location = new System.Drawing.Point(12, 233); + this.cartesianChart1.Name = "cartesianChart1"; + this.cartesianChart1.Size = new System.Drawing.Size(533, 292); + this.cartesianChart1.TabIndex = 29; + this.cartesianChart1.Text = "cartesianChart1"; + // + // BSNESTraceLogBinaryMonitorForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(557, 537); + this.Controls.Add(this.cartesianChart1); + this.Controls.Add(this.button1); + this.Controls.Add(this.pictureGreenSpinner); + this.Controls.Add(this.lblResultStatus); + this.Controls.Add(this.label7); + this.Controls.Add(this.label17); + this.Controls.Add(this.label16); + this.Controls.Add(this.label15); + this.Controls.Add(this.lblModifiedFlags); + this.Controls.Add(this.label14); + this.Controls.Add(this.lblModifiedDBs); + this.Controls.Add(this.label12); + this.Controls.Add(this.lblModifiedDPs); + this.Controls.Add(this.label10); + this.Controls.Add(this.lblModifiedMFlags); + this.Controls.Add(this.label8); + this.Controls.Add(this.lblModifiedXFlags); + this.Controls.Add(this.label6); + this.Controls.Add(this.lblNumberModified); + this.Controls.Add(this.label3); + this.Controls.Add(this.lblStatus); + this.Controls.Add(this.label5); + this.Controls.Add(this.lblTotalProcessed); + this.Controls.Add(this.label4); + this.Controls.Add(this.btnFinish); + this.Controls.Add(this.lblQueueSize); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnStart); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "BsnesTraceLogBinaryMonitorForm"; + this.Text = "BSNES Live Tracelog Capture"; + this.Load += new System.EventHandler(this.BSNESTraceLogBinaryMonitorForm_Load); + this.Shown += new System.EventHandler(this.BSNESTraceLogBinaryMonitorForm_Shown); + ((System.ComponentModel.ISupportInitialize)(this.pictureGreenSpinner)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnStart; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblQueueSize; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.Button btnFinish; + private System.Windows.Forms.Label lblTotalProcessed; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label lblStatus; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label lblNumberModified; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label lblModifiedXFlags; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label lblModifiedMFlags; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label lblModifiedDPs; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label lblModifiedDBs; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label lblModifiedFlags; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.Label lblResultStatus; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.PictureBox pictureGreenSpinner; + private System.Windows.Forms.Button button1; + private LiveCharts.WinForms.CartesianChart cartesianChart1; + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.UI.cs b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.UI.cs new file mode 100644 index 00000000..1c508806 --- /dev/null +++ b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.UI.cs @@ -0,0 +1,123 @@ +using System; +using System.Windows.Forms; +using System.Windows.Media; +using ByteSizeLib; +using Diz.Core.import; +using LiveCharts; +using LiveCharts.Wpf; +using Color = System.Drawing.Color; + +namespace DiztinGUIsh.window.dialog +{ + public partial class BsnesTraceLogBinaryMonitorForm + { + private bool initializedChart; + private readonly ChartValues chartValuesBytesModified = new ChartValues(); + //private long chartValueBytesModified_previous = 0; + + //private const int refreshGraphEveryNDataPoints = 100; + //private int dataPointsIn = -1; + + private void AppendToChart((BsnesTraceLogImporter.Stats stats, int bytesInQueue) currentStats) + { + InitChart(); + + // TODO: re-enable, it's maybe a little slow. + /*if (dataPointsIn == -1 || ++dataPointsIn >= refreshGraphEveryNDataPoints) + dataPointsIn = 0; + + if (dataPointsIn != 0) + return; + + var diffBytes = currentStats.stats.numRomBytesModified - chartValueBytesModified_previous; + chartValueBytesModified_previous = currentStats.stats.numRomBytesModified; + chartValuesBytesModified.Add(diffBytes);*/ + } + + private void InitChart() + { + if (initializedChart) + return; + + cartesianChart1.Series = new SeriesCollection + { + new LineSeries + { + Title = "Instructions Modified", + Values = chartValuesBytesModified, + PointGeometry = Geometry.Empty, + }, + }; + + cartesianChart1.DisableAnimations = true; + + initializedChart = true; + } + + private void UpdateUi() + { + var running = capturing?.Running ?? false; + var finishing = capturing?.Finishing ?? false; + + lblStatus.Text = !running ? "Not running" : finishing ? "Stopping..." : "Running"; + + btnFinish.Enabled = !finishing && running; + btnStart.Enabled = !running; + + pictureGreenSpinner.Visible = pictureGreenSpinner.Enabled = running; + + if (running) + { + lblResultStatus.Text = ""; + } + else if (lastError != "") + { + lblResultStatus.Text = lastError; + lblResultStatus.ForeColor = Color.Red; + } + else + { + lblResultStatus.Text = "Success!"; + lblResultStatus.ForeColor = Color.ForestGreen; + } + + if (capturing == null) + return; + + var currentStats = capturing.GetStats(); + var (stats, totalQueueBytes) = currentStats; + + AppendToChart(currentStats); + + var qItemCount = capturing.BlocksToProcess.ToString(); + var qByteCount = ByteSize.FromBytes(totalQueueBytes).ToString("0.0"); + + lblQueueSize.Text = $"{qByteCount} (num groups: {qItemCount})"; + + // TODO: use databinding + + lblTotalProcessed.Text = ByteSize.FromBytes(stats.NumRomBytesAnalyzed).ToString("0.00"); + lblNumberModified.Text = ByteSize.FromBytes(stats.NumRomBytesModified).ToString("0.00"); + lblModifiedDBs.Text = ByteSize.FromBytes(stats.NumDbModified).ToString("0.00"); + lblModifiedDPs.Text = ByteSize.FromBytes(stats.NumDpModified).ToString("0.00"); + lblModifiedFlags.Text = ByteSize.FromBytes(stats.NumMarksModified).ToString("0.00"); + lblModifiedXFlags.Text = ByteSize.FromBytes(stats.NumXFlagsModified).ToString("0.00"); + lblModifiedMFlags.Text = ByteSize.FromBytes(stats.NumMFlagsModified).ToString("0.00"); + } + + private void button1_Click(object sender, EventArgs e) + { + MessageBox.Show("What is this? \r\n" + + "Connect via socket to a special build of BSNES-plus and capture live tracelog as you play the game " + + "in realtime or play back a movie/recording/TAS.\r\n\r\n" + + "As each instruction is visited by the CPU, info like X,M,DB,D and flags are capture and " + + "logged in Diz. This will greatly aid in dissasembly.\r\n\r\n" + + "If you're just starting a ROM hacking project from scratch, you want to see this" + + " capture a lot of modified data for X,M,DP,DB and marking bytes as Opcode/Operands.\r\n\r\n" + + "If you're far into a ROM hacking project, you will start seeing fewer NEWLY DISCOVERED " + + "modifications here. Try playing through different parts of the game, menus, every " + + "combination of searching you can do to allow this tool to discover as much as it can.\r\n\r\n" + + "When you close this window, try exporting your disassembly and see how much you uncovered!\r\n"); + } + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.cs b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.cs new file mode 100644 index 00000000..b7681860 --- /dev/null +++ b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.cs @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using Diz.Core.import; + +namespace DiztinGUIsh.window.dialog +{ + // TODO: add controller/view for this. + // + // TODO: BSNESTraceLogCapture does a lot of threading. It's decently protected but, + // while that stuff is running, try and avoid using 'Data' anywhere outside BSNESTraceLogCapture. + // eventually, if we want to do that we need to retrofit the rest of the app to take advantage of that. + public partial class BsnesTraceLogBinaryMonitorForm : Form + { + private readonly MainWindow mainWindow; + private BsnesTraceLogCapture capturing; + private string lastError; + + public BsnesTraceLogBinaryMonitorForm(MainWindow window) + { + mainWindow = window; + InitializeComponent(); + } + + private void btnStart_Click(object sender, EventArgs e) + { + timer1.Enabled = true; + btnFinish.Enabled = true; + btnStart.Enabled = false; + + capturing = new BsnesTraceLogCapture(); + + Start(); + } + + private async void Start() + { + // TODO: error handling is busted here. + await Task.Run(() => { + capturing.Run(mainWindow.Project.Data); + }).ContinueWith(task => { + this.InvokeIfRequired(() => CapturingFinished(task.Exception)); + }); + UpdateUi(); + } + + private void btnFinish_Click(object sender, EventArgs e) + { + capturing?.SignalToStop(); + UpdateUi(); + } + + private void CapturingFinished(AggregateException ex) + { + if (ex != null) { + OnError(ex); + } + + timer1.Enabled = false; + capturing = null; + UpdateUi(); + } + + private void timer1_Tick(object sender, EventArgs e) => UpdateUi(); + + private void OnError(AggregateException e) + { + Console.WriteLine(e.ToString()); + lastError = e.InnerExceptions.Select(ex => ex.Message).Aggregate((line, val) => line += val + "\n"); + } + + private void BSNESTraceLogBinaryMonitorForm_Load(object sender, EventArgs e) => UpdateUi(); + private void BSNESTraceLogBinaryMonitorForm_Shown(object sender, EventArgs e) => UpdateUi(); + } +} \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.resx b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.resx new file mode 100644 index 00000000..c866993a --- /dev/null +++ b/DiztinGUIsh/window/dialog/BSNESTraceLogBinaryMonitorForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 248, 17 + + + 36 + + \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/ExportDisassembly.Designer.cs b/DiztinGUIsh/window/dialog/ExportDisassembly.Designer.cs index ba0d1c9b..73297ae4 100644 --- a/DiztinGUIsh/window/dialog/ExportDisassembly.Designer.cs +++ b/DiztinGUIsh/window/dialog/ExportDisassembly.Designer.cs @@ -44,6 +44,8 @@ private void InitializeComponent() this.numData = new System.Windows.Forms.NumericUpDown(); this.chkPrintLabelSpecificComments = new System.Windows.Forms.CheckBox(); this.chkIncludeUnusedLabels = new System.Windows.Forms.CheckBox(); + this.saveLogSingleFile = new System.Windows.Forms.SaveFileDialog(); + this.chooseLogFolder = new System.Windows.Forms.FolderBrowserDialog(); ((System.ComponentModel.ISupportInitialize)(this.numData)).BeginInit(); this.SuspendLayout(); // @@ -216,8 +218,13 @@ private void InitializeComponent() this.chkIncludeUnusedLabels.UseVisualStyleBackColor = true; this.chkIncludeUnusedLabels.CheckedChanged += new System.EventHandler(this.chkIncludeUnusedLabels_CheckedChanged); // + // saveLogSingleFile + // + this.saveLogSingleFile.Filter = "Assembly Files|*.asm|All Files|*.*"; + // // ExportDisassembly // + this.AcceptButton = this.button2; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancel; @@ -265,5 +272,7 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown numData; private System.Windows.Forms.CheckBox chkPrintLabelSpecificComments; private System.Windows.Forms.CheckBox chkIncludeUnusedLabels; + private System.Windows.Forms.SaveFileDialog saveLogSingleFile; + private System.Windows.Forms.FolderBrowserDialog chooseLogFolder; } } \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/ExportDisassembly.cs b/DiztinGUIsh/window/dialog/ExportDisassembly.cs index 7c7ac70b..f0a0d8de 100644 --- a/DiztinGUIsh/window/dialog/ExportDisassembly.cs +++ b/DiztinGUIsh/window/dialog/ExportDisassembly.cs @@ -1,28 +1,54 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; +using Diz.Core.export; +using Diz.Core.model; +using Diz.Core.util; namespace DiztinGUIsh { + // consider renaming? this class is mostly about editing settings, with a 'save' button at the + // end. public partial class ExportDisassembly : Form { - public ExportDisassembly() + private readonly Project project; + + // Our copy. At the end, if everything is correct, we'll return this. + private LogWriterSettings settings; + + // shows the UI and returns non-null settings if everything went OK in the + // setup process. + public static LogWriterSettings? ConfirmSettingsAndAskToStart(Project project) { + var export = new ExportDisassembly(project); + if (export.ShowDialog() != DialogResult.OK) + return null; + + return export.settings; + } + + public ExportDisassembly(Project project) + { + this.project = project; + settings = project.LogWriterSettings; // copy + + if (settings.Validate() != "") + settings.SetDefaults(); + InitializeComponent(); - numData.Value = LogCreator.dataPerLine; - textFormat.Text = LogCreator.format; - comboUnlabeled.SelectedIndex = (int)LogCreator.unlabeled; - comboStructure.SelectedIndex = (int)LogCreator.structure; - chkIncludeUnusedLabels.Checked = LogCreator.includeUnusedLabels; - chkPrintLabelSpecificComments.Checked = LogCreator.printLabelSpecificComments; - UpdateSample(); + UpdateUiFromProjectSettings(); + RegenerateSampleOutput(); + } + + public void UpdateUiFromProjectSettings() + { + // TODO: in the future, replace this with databinding so we don't have to do it manually + numData.Value = settings.DataPerLine; + textFormat.Text = settings.Format; + comboUnlabeled.SelectedIndex = (int)settings.Unlabeled; + comboStructure.SelectedIndex = (int)settings.Structure; + chkIncludeUnusedLabels.Checked = settings.IncludeUnusedLabels; + chkPrintLabelSpecificComments.Checked = settings.PrintLabelSpecificComments; } private void cancel_Click(object sender, EventArgs e) @@ -32,18 +58,53 @@ private void cancel_Click(object sender, EventArgs e) private void button2_Click(object sender, EventArgs e) { + if (!PromptForPath()) + return; + this.DialogResult = DialogResult.OK; } + // Prompt user for either a filename to save, or a folder location + private string PromptForLogPathFromFileOrFolderDialog(bool askForFile) + { + return askForFile ? PromptSaveLogFile() : PromptSaveLogPath(); + } + + private string PromptSaveLogPath() + { + chooseLogFolder.SelectedPath = Path.GetDirectoryName(project.ProjectFileName); + return chooseLogFolder.ShowDialog() == DialogResult.OK && chooseLogFolder.SelectedPath != "" + ? chooseLogFolder.SelectedPath : null; + } + + private string PromptSaveLogFile() + { + saveLogSingleFile.InitialDirectory = project.ProjectFileName; + return saveLogSingleFile.ShowDialog() == DialogResult.OK && saveLogSingleFile.FileName != "" + ? saveLogSingleFile.FileName : null; + } + + private bool PromptForPath() + { + var singleFile = settings.Structure == LogCreator.FormatStructure.SingleFile; + var fileOrFolderPath = PromptForLogPathFromFileOrFolderDialog(singleFile); + + if (string.IsNullOrEmpty(fileOrFolderPath)) + return false; + + settings.FileOrFolderOutPath = fileOrFolderPath; + + return true; + } + private void textFormat_TextChanged(object sender, EventArgs e) { if (ValidateFormat()) { - LogCreator.format = textFormat.Text.ToLower(); - UpdateSample(); + settings.Format = textFormat.Text.ToLower(); + RegenerateSampleOutput(); button2.Enabled = true; - } else - { + } else { textSample.Text = "Invalid format!"; button2.Enabled = false; } @@ -51,228 +112,40 @@ private void textFormat_TextChanged(object sender, EventArgs e) private void numData_ValueChanged(object sender, EventArgs e) { - LogCreator.dataPerLine = (int)numData.Value; - UpdateSample(); + settings.DataPerLine = (int)numData.Value; + RegenerateSampleOutput(); } private void comboUnlabeled_SelectedIndexChanged(object sender, EventArgs e) { - LogCreator.unlabeled = (LogCreator.FormatUnlabeled)comboUnlabeled.SelectedIndex; - UpdateSample(); + settings.Unlabeled = (LogCreator.FormatUnlabeled)comboUnlabeled.SelectedIndex; + RegenerateSampleOutput(); } private void comboStructure_SelectedIndexChanged(object sender, EventArgs e) { - LogCreator.structure = (LogCreator.FormatStructure)comboStructure.SelectedIndex; + settings.Structure = (LogCreator.FormatStructure)comboStructure.SelectedIndex; } private bool ValidateFormat() { - string[] tokens = textFormat.Text.ToLower().Split('%'); - - // not valid if format has an odd amount of %s - if (tokens.Length % 2 == 0) return false; - - for (int i = 1; i < tokens.Length; i += 2) - { - int indexOfColon = tokens[i].IndexOf(':'); - string kind = indexOfColon >= 0 ? tokens[i].Substring(0, indexOfColon) : tokens[i]; - - // not valid if base token isn't one we know of - if (!LogCreator.parameters.ContainsKey(kind)) return false; - - // not valid if parameter isn't an integer - if (indexOfColon >= 0 && !int.TryParse(tokens[i].Substring(indexOfColon + 1), out int oof)) return false; - } - - return true; + return LogCreator.ValidateFormat(textFormat.Text); } - // https://stackoverflow.com/a/29679597 - private void UpdateSample() + private void RegenerateSampleOutput() { - // cheeky way of using the same methods for disassembling a different set of data :^) - while (sampleTable.Count < 0x8000) sampleTable.Add(new ROMByte()); - - using (MemoryStream mem = new MemoryStream()) - using (StreamWriter sw = new StreamWriter(mem)) - { - List tempTable = Data.GetTable(); - Data.ROMMapMode tempMode = Data.GetROMMapMode(); - Data.ROMSpeed tempSpeed = Data.GetROMSpeed(); - var tempAlias = Data.GetAllLabels(); - var tempComment = Data.GetAllComments(); - LogCreator.FormatStructure tempStructure = LogCreator.structure; - Data.Restore(sampleTable, Data.ROMMapMode.LoROM, Data.ROMSpeed.FastROM, sampleAlias, sampleComment); - LogCreator.structure = LogCreator.FormatStructure.SingleFile; - - LogCreator.CreateLog(sw, StreamWriter.Null); - - Data.Restore(tempTable, tempMode, tempSpeed, tempAlias, tempComment); - LogCreator.structure = tempStructure; - - sw.Flush(); - mem.Seek(0, SeekOrigin.Begin); - - textSample.Text = Encoding.UTF8.GetString(mem.ToArray(), 0, (int)mem.Length); - } + var result = RomUtil.GetSampleAssemblyOutput(settings); + textSample.Text = result.OutputStr; } - // random sample code I made up; hopefully it shows a little bit of - // everything so you can see how the settings will effect the output - public static List sampleTable = new List - { - new ROMByte {Rom = 0x78, TypeFlag = Data.FlagType.Opcode, MFlag = true, XFlag = true, Point = Data.InOutPoint.InPoint}, - new ROMByte {Rom = 0xA9, TypeFlag = Data.FlagType.Opcode, MFlag = true, XFlag = true}, - new ROMByte {Rom = 0x01, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x8D, TypeFlag = Data.FlagType.Opcode, MFlag = true, XFlag = true}, - new ROMByte {Rom = 0x0D, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x42, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x5C, TypeFlag = Data.FlagType.Opcode, MFlag = true, XFlag = true, Point = Data.InOutPoint.EndPoint}, - new ROMByte {Rom = 0x0A, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0xC2, TypeFlag = Data.FlagType.Opcode, MFlag = true, XFlag = true, Point = Data.InOutPoint.InPoint}, - new ROMByte {Rom = 0x30, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0xA9, TypeFlag = Data.FlagType.Opcode}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x21, TypeFlag = Data.FlagType.Operand}, - new ROMByte {Rom = 0x5B, TypeFlag = Data.FlagType.Opcode}, - new ROMByte {Rom = 0x4B, TypeFlag = Data.FlagType.Opcode, DirectPage = 0x2100}, - new ROMByte {Rom = 0xAB, TypeFlag = Data.FlagType.Opcode, DirectPage = 0x2100}, - new ROMByte {Rom = 0xA2, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x07, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xBF, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x32, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x9F, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x7E, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xCA, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xCA, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x10, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xF4, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x64, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x40, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x64, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x41, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x64, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x42, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x64, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x43, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xAE, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xFC, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x3A, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x4C, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.EndPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xC0, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Data16Bit, Point = Data.InOutPoint.ReadPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x08, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x10, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x20, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x44, TypeFlag = Data.FlagType.Pointer16Bit, Point = Data.InOutPoint.ReadPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x7B, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x44, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x81, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xC4, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x81, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x0A, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x82, TypeFlag = Data.FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x08, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x8B, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x4B, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xAB, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xE2, TypeFlag = Data.FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x20, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xC2, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x10, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xA2, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x1F, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xBD, TypeFlag = Data.FlagType.Opcode, MFlag = true, Point = Data.InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x5B, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x80, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x9D, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x01, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xCA, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x10, TypeFlag = Data.FlagType.Opcode, MFlag = true, Point = Data.InOutPoint.OutPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xF7, TypeFlag = Data.FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xAB, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x28, TypeFlag = Data.FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x60, TypeFlag = Data.FlagType.Opcode, Point = Data.InOutPoint.EndPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x45, TypeFlag = Data.FlagType.Data8Bit, Point = Data.InOutPoint.ReadPoint, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x8D, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x69, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x83, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xB2, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x99, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x00, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x23, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x01, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xA3, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xF8, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x52, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x08, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xBB, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x29, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x5C, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x32, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xE7, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x88, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x3C, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x30, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x18, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x9A, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xB0, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x34, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x8C, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xDD, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x05, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0xB7, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x83, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x34, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - new ROMByte {Rom = 0x6D, TypeFlag = Data.FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100}, - }; - - public static Dictionary sampleAlias = new Dictionary - { - { 0x00, new Data.AliasInfo() {name="Emulation_RESET", comment="Sample emulation reset location"} }, - { 0x0A, new Data.AliasInfo() {name="FastRESET", comment="Sample label" } }, - { 0x32, new Data.AliasInfo() {name="Test_Indices"} }, - { 0x3A, new Data.AliasInfo() {name="Pointer_Table"} }, - { 0x44, new Data.AliasInfo() {name="First_Routine"} }, - { 0x5B, new Data.AliasInfo() {name="Test_Data", comment="Pretty cool huh?" } } - }; - - public static Dictionary sampleComment = new Dictionary - { - { 0x03, "this sets FastROM" }, - { 0x0F, "direct page = $2100" }, - { 0x21, "clear APU regs" }, - { 0x44, "this routine copies Test_Data to $7E0100" } - }; - private void chkPrintLabelSpecificComments_CheckedChanged(object sender, EventArgs e) { - LogCreator.printLabelSpecificComments = chkPrintLabelSpecificComments.Checked; + settings.PrintLabelSpecificComments = chkPrintLabelSpecificComments.Checked; } private void chkIncludeUnusedLabels_CheckedChanged(object sender, EventArgs e) { - LogCreator.includeUnusedLabels = chkIncludeUnusedLabels.Checked; + settings.IncludeUnusedLabels = chkIncludeUnusedLabels.Checked; } } } diff --git a/DiztinGUIsh/window/dialog/ExportDisassembly.resx b/DiztinGUIsh/window/dialog/ExportDisassembly.resx index 5294a816..aa574a0f 100644 --- a/DiztinGUIsh/window/dialog/ExportDisassembly.resx +++ b/DiztinGUIsh/window/dialog/ExportDisassembly.resx @@ -124,4 +124,10 @@ the instruction misalignment checker and the in/out point scanner before exporting. See the help document for more details. + + 41, 29 + + + 201, 26 + \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/GotoDialog.Designer.cs b/DiztinGUIsh/window/dialog/GotoDialog.Designer.cs index 970ff256..e0343fc0 100644 --- a/DiztinGUIsh/window/dialog/GotoDialog.Designer.cs +++ b/DiztinGUIsh/window/dialog/GotoDialog.Designer.cs @@ -1,4 +1,4 @@ -namespace DiztinGUIsh +namespace DiztinGUIsh.window.dialog { partial class GotoDialog { @@ -138,6 +138,7 @@ private void InitializeComponent() // // GotoDialog // + this.AcceptButton = this.go; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancel; diff --git a/DiztinGUIsh/window/dialog/GotoDialog.cs b/DiztinGUIsh/window/dialog/GotoDialog.cs index cc2ef183..bd6e82cb 100644 --- a/DiztinGUIsh/window/dialog/GotoDialog.cs +++ b/DiztinGUIsh/window/dialog/GotoDialog.cs @@ -1,29 +1,26 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; -namespace DiztinGUIsh +namespace DiztinGUIsh.window.dialog { public partial class GotoDialog : Form { - public GotoDialog(int offset) + public Data Data { get; set; } + public GotoDialog(int offset, Data data) { InitializeComponent(); - textROM.Text = Util.NumberToBaseString(Util.ConvertPCtoSNES(offset), Util.NumberBase.Hexadecimal, 6); + Data = data; + textROM.Text = Util.NumberToBaseString(Data.ConvertPCtoSnes(offset), Util.NumberBase.Hexadecimal, 6); textPC.Text = Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 0); } private void GotoDialog_Load(object sender, EventArgs e) { textROM.SelectAll(); - UpdateUI(); + UpdateUi(); } private int ParseOffset(string text) @@ -43,7 +40,7 @@ private void Go() this.DialogResult = DialogResult.OK; } - private bool updatingText = false; + private bool updatingText; private bool UpdateTextChanged(string txtChanged, Action onSuccess) { @@ -52,10 +49,10 @@ private bool UpdateTextChanged(string txtChanged, Action= 0) + if (ByteUtil.StripFormattedAddress(ref txtChanged, style, out var address) && address >= 0) { onSuccess(txtChanged, address, noBase); result = true; @@ -70,12 +67,12 @@ private bool UpdateTextChanged(string txtChanged, Action= 0 && pc < Data.GetROMSize(); + return pc >= 0 && pc < Data.GetRomSize(); } - private bool IsPCOffsetValid() + private bool IsPcOffsetValid() { var offset = GetPcOffset(); - return IsValidPCAddress(offset); + return IsValidPcAddress(offset); } private bool IsRomAddressValid() @@ -107,33 +104,33 @@ private bool IsRomAddressValid() if (address < 0) return false; - return IsValidPCAddress(Util.ConvertSNEStoPC(address)); + return IsValidPcAddress(Data.ConvertSnesToPc(address)); } private void textROM_TextChanged(object sender, EventArgs e) { UpdateTextChanged(textROM.Text,(finaltext, address, noBase) => { - int pc = Util.ConvertSNEStoPC(address); + int pc = Data.ConvertSnesToPc(address); textROM.Text = finaltext; textPC.Text = Util.NumberToBaseString(pc, noBase, 0); }); - UpdateUI(); + UpdateUi(); } private void textPC_TextChanged(object sender, EventArgs e) { UpdateTextChanged(textPC.Text, (finaltext, offset, noBase) => { - int addr = Util.ConvertPCtoSNES(offset); + int addr = Data.ConvertPCtoSnes(offset); textPC.Text = finaltext; textROM.Text = Util.NumberToBaseString(addr, noBase, 6); }); - UpdateUI(); + UpdateUi(); } private void go_Click(object sender, EventArgs e) @@ -158,15 +155,13 @@ private void textPC_KeyDown(object sender, KeyEventArgs e) private void radioHex_CheckedChanged(object sender, EventArgs e) { - if (radioHex.Checked) - { - if (int.TryParse(textPC.Text, out int result)) + if (radioHex.Checked) { + if (int.TryParse(textPC.Text, out var result)) { textPC.Text = Util.NumberToBaseString(result, Util.NumberBase.Hexadecimal, 0); } - } else - { - if (int.TryParse(textPC.Text, NumberStyles.HexNumber, null, out int result)) + } else { + if (int.TryParse(textPC.Text, NumberStyles.HexNumber, null, out var result)) { textPC.Text = result.ToString(); } diff --git a/DiztinGUIsh/window/dialog/HarshAutoStep.Designer.cs b/DiztinGUIsh/window/dialog/HarshAutoStep.Designer.cs index 4eb5afae..d182f8af 100644 --- a/DiztinGUIsh/window/dialog/HarshAutoStep.Designer.cs +++ b/DiztinGUIsh/window/dialog/HarshAutoStep.Designer.cs @@ -196,6 +196,7 @@ private void InitializeComponent() // // HarshAutoStep // + this.AcceptButton = this.go; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancel; diff --git a/DiztinGUIsh/window/dialog/HarshAutoStep.cs b/DiztinGUIsh/window/dialog/HarshAutoStep.cs index 3ca960f1..a9fac3e0 100644 --- a/DiztinGUIsh/window/dialog/HarshAutoStep.cs +++ b/DiztinGUIsh/window/dialog/HarshAutoStep.cs @@ -1,59 +1,50 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; +using System.Diagnostics; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; namespace DiztinGUIsh { public partial class HarshAutoStep : Form { - private int start, end, count; - - public HarshAutoStep(int offset) + public int Start { get; private set; } + public int End { get; private set; } + public int Count { get; private set; } + + private readonly Data data; + private bool updatingText; + + public HarshAutoStep(int offset, Data data) { + Debug.Assert(data != null); + this.data = data; + InitializeComponent(); - start = offset; - int rest = Data.GetROMSize() - start; - count = rest < 0x100 ? rest : 0x100; - end = start + count; + Start = offset; + var rest = data.GetRomSize() - Start; + Count = rest < 0x100 ? rest : 0x100; + End = Start + Count; UpdateText(null); } - public int GetOffset() - { - return start; - } - - public int GetCount() - { - return count; - } - - private bool updatingText = false; - private void UpdateText(TextBox selected) { - Util.NumberBase noBase = radioDec.Checked ? Util.NumberBase.Decimal : Util.NumberBase.Hexadecimal; - int digits = noBase == Util.NumberBase.Hexadecimal && radioROM.Checked ? 6 : 0; - int size = Data.GetROMSize(); + var noBase = radioDec.Checked ? Util.NumberBase.Decimal : Util.NumberBase.Hexadecimal; + var digits = noBase == Util.NumberBase.Hexadecimal && radioROM.Checked ? 6 : 0; - if (start < 0) start = 0; - if (end >= size) end = size - 1; - count = end - start; - if (count < 0) count = 0; + if (Start < 0) Start = 0; + if (End >= data.GetRomSize()) End = data.GetRomSize() - 1; + Count = End - Start; + if (Count < 0) Count = 0; updatingText = true; - if (selected != textStart) textStart.Text = Util.NumberToBaseString(radioROM.Checked ? Util.ConvertPCtoSNES(start) : start, noBase, digits); - if (selected != textEnd) textEnd.Text = Util.NumberToBaseString(radioROM.Checked ? Util.ConvertPCtoSNES(end) : end, noBase, digits); - if (selected != textCount) textCount.Text = Util.NumberToBaseString(count, noBase, 0); + if (selected != textStart) textStart.Text = Util.NumberToBaseString(radioROM.Checked ? data.ConvertPCtoSnes(Start) : Start, noBase, digits); + if (selected != textEnd) textEnd.Text = Util.NumberToBaseString(radioROM.Checked ? data.ConvertPCtoSnes(End) : End, noBase, digits); + if (selected != textCount) textCount.Text = Util.NumberToBaseString(Count, noBase, 0); updatingText = false; } @@ -69,68 +60,59 @@ private void radioROM_CheckedChanged(object sender, EventArgs e) private void textCount_TextChanged(object sender, EventArgs e) { - if (!updatingText) + OnTextChanged(textCount, value => { - updatingText = true; - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; - - int result = 0; - if (int.TryParse(textCount.Text, style, null, out result)) - { - count = result; - end = start + count; - } - - UpdateText(textCount); - } + Count = value; + End = Start + Count; + }); } private void textEnd_TextChanged(object sender, EventArgs e) { - if (!updatingText) + OnTextChanged(textEnd, value => { - updatingText = true; - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; - - int result = 0; - if (int.TryParse(textEnd.Text, style, null, out result)) - { - if (radioROM.Checked) result = Util.ConvertSNEStoPC(result); - end = result; - count = end - start; - } - - UpdateText(textEnd); - } + if (radioROM.Checked) + value = data.ConvertSnesToPc(value); + + End = value; + Count = End - Start; + }); } private void textStart_TextChanged(object sender, EventArgs e) { - if (!updatingText) + OnTextChanged(textStart, value => { - updatingText = true; - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; - - int result = 0; - if (int.TryParse(textStart.Text, style, null, out result)) - { - if (radioROM.Checked) result = Util.ConvertSNEStoPC(result); - start = result; - count = end - start; - } - - UpdateText(textStart); - } + if (radioROM.Checked) + value = data.ConvertSnesToPc(value); + + Start = value; + Count = End - Start; + }); + } + + private void OnTextChanged(TextBox textBox, Action OnResult) + { + if (updatingText) + return; + + updatingText = true; + var style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; + + if (int.TryParse(textBox.Text, style, null, out var result)) + OnResult(result); + + UpdateText(textBox); } private void go_Click(object sender, EventArgs e) { - this.DialogResult = DialogResult.OK; + DialogResult = DialogResult.OK; } private void cancel_Click(object sender, EventArgs e) { - this.Close(); + Close(); } } } diff --git a/DiztinGUIsh/window/dialog/ImportROMDialog.Designer.cs b/DiztinGUIsh/window/dialog/ImportROMDialog.Designer.cs index 330545b2..413b3155 100644 --- a/DiztinGUIsh/window/dialog/ImportROMDialog.Designer.cs +++ b/DiztinGUIsh/window/dialog/ImportROMDialog.Designer.cs @@ -1,6 +1,6 @@ -namespace DiztinGUIsh +namespace DiztinGUIsh.window.dialog { - partial class ImportROMDialog + partial class ImportRomDialog { /// /// Required designer variable. @@ -28,19 +28,13 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { + System.Windows.Forms.Label sourceLabel; this.detectMessage = new System.Windows.Forms.Label(); this.checkData = new System.Windows.Forms.Label(); - this.comboBox1 = new System.Windows.Forms.ComboBox(); - this.mapmode = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); this.cancel = new System.Windows.Forms.Button(); this.okay = new System.Windows.Forms.Button(); - this.romspeed = new System.Windows.Forms.Label(); - this.romtitle = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.label13 = new System.Windows.Forms.Label(); this.checkboxEmuIRQ = new System.Windows.Forms.CheckBox(); @@ -76,79 +70,51 @@ private void InitializeComponent() this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.checkHeader = new System.Windows.Forms.CheckBox(); - this.groupBox1.SuspendLayout(); + this.label14 = new System.Windows.Forms.Label(); + this.romspeed = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.romtitle = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.cmbRomMapMode = new System.Windows.Forms.ComboBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + sourceLabel = new System.Windows.Forms.Label(); this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); this.SuspendLayout(); // + // sourceLabel + // + sourceLabel.AutoSize = true; + sourceLabel.Location = new System.Drawing.Point(14, 28); + sourceLabel.Name = "sourceLabel"; + sourceLabel.Size = new System.Drawing.Size(83, 13); + sourceLabel.TabIndex = 5; + sourceLabel.Text = "Rom Map Mode"; + // // detectMessage // this.detectMessage.AutoSize = true; - this.detectMessage.Location = new System.Drawing.Point(36, 7); + this.detectMessage.Location = new System.Drawing.Point(123, 7); this.detectMessage.Name = "detectMessage"; - this.detectMessage.Size = new System.Drawing.Size(187, 13); + this.detectMessage.Size = new System.Drawing.Size(85, 13); this.detectMessage.TabIndex = 0; - this.detectMessage.Text = "Couldn\'t auto detect ROM Map Mode!"; + this.detectMessage.Text = "[detectedStatus]"; this.detectMessage.TextAlign = System.Drawing.ContentAlignment.TopCenter; // // checkData // this.checkData.AutoSize = true; - this.checkData.Location = new System.Drawing.Point(39, 23); + this.checkData.Location = new System.Drawing.Point(39, 29); this.checkData.Name = "checkData"; this.checkData.Size = new System.Drawing.Size(179, 13); this.checkData.TabIndex = 1; this.checkData.Text = "Does the following info look correct?"; this.checkData.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // comboBox1 - // - this.comboBox1.FormattingEnabled = true; - this.comboBox1.Items.AddRange(new object[] { - "(0) LoROM", - "(1) HiROM", - "(2) Super MMC", - "(3) SA-1 ROM", - "(3) SA-1 ROM (FuSoYa\'s 8MB mapper)", - "(4) SuperFX", - "(5) ExHiROM", - "(6) ExLoROM"}); - this.comboBox1.Location = new System.Drawing.Point(103, 18); - this.comboBox1.Name = "comboBox1"; - this.comboBox1.Size = new System.Drawing.Size(121, 21); - this.comboBox1.TabIndex = 1; - this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); - // - // mapmode - // - this.mapmode.AutoSize = true; - this.mapmode.Location = new System.Drawing.Point(11, 21); - this.mapmode.Name = "mapmode"; - this.mapmode.Size = new System.Drawing.Size(89, 13); - this.mapmode.TabIndex = 0; - this.mapmode.Text = "ROM Map Mode:"; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(31, 44); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(69, 13); - this.label4.TabIndex = 2; - this.label4.Text = "ROM Speed:"; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(34, 66); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(66, 13); - this.label5.TabIndex = 4; - this.label5.Text = "ROM Name:"; - // // cancel // this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancel.Location = new System.Drawing.Point(12, 388); + this.cancel.Location = new System.Drawing.Point(12, 394); this.cancel.Name = "cancel"; this.cancel.Size = new System.Drawing.Size(75, 23); this.cancel.TabIndex = 8; @@ -159,7 +125,7 @@ private void InitializeComponent() // // okay // - this.okay.Location = new System.Drawing.Point(172, 388); + this.okay.Location = new System.Drawing.Point(172, 394); this.okay.Name = "okay"; this.okay.Size = new System.Drawing.Size(75, 23); this.okay.TabIndex = 7; @@ -167,28 +133,10 @@ private void InitializeComponent() this.okay.UseVisualStyleBackColor = true; this.okay.Click += new System.EventHandler(this.okay_Click); // - // romspeed - // - this.romspeed.AutoSize = true; - this.romspeed.Location = new System.Drawing.Point(103, 44); - this.romspeed.Name = "romspeed"; - this.romspeed.Size = new System.Drawing.Size(55, 13); - this.romspeed.TabIndex = 3; - this.romspeed.Text = "SlowROM"; - // - // romtitle - // - this.romtitle.AutoSize = true; - this.romtitle.Location = new System.Drawing.Point(103, 66); - this.romtitle.Name = "romtitle"; - this.romtitle.Size = new System.Drawing.Size(27, 13); - this.romtitle.TabIndex = 5; - this.romtitle.Text = "Title"; - // // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(32, 39); + this.label3.Location = new System.Drawing.Point(32, 45); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(198, 13); this.label3.TabIndex = 2; @@ -198,28 +146,13 @@ private void InitializeComponent() // label6 // this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(3, 55); + this.label6.Location = new System.Drawing.Point(3, 61); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(255, 13); this.label6.TabIndex = 3; this.label6.Text = "This cannot be changed once the project is created."; this.label6.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // groupBox1 - // - this.groupBox1.Controls.Add(this.mapmode); - this.groupBox1.Controls.Add(this.comboBox1); - this.groupBox1.Controls.Add(this.label4); - this.groupBox1.Controls.Add(this.romtitle); - this.groupBox1.Controls.Add(this.label5); - this.groupBox1.Controls.Add(this.romspeed); - this.groupBox1.Location = new System.Drawing.Point(12, 74); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(235, 89); - this.groupBox1.TabIndex = 4; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "ROM Information"; - // // groupBox2 // this.groupBox2.Controls.Add(this.label13); @@ -255,9 +188,9 @@ private void InitializeComponent() this.groupBox2.Controls.Add(this.label7); this.groupBox2.Controls.Add(this.label2); this.groupBox2.Controls.Add(this.label1); - this.groupBox2.Location = new System.Drawing.Point(12, 169); + this.groupBox2.Location = new System.Drawing.Point(12, 175); this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(235, 189); + this.groupBox2.Size = new System.Drawing.Size(244, 189); this.groupBox2.TabIndex = 5; this.groupBox2.TabStop = false; this.groupBox2.Text = "Vectors @ $00FFE0"; @@ -265,7 +198,7 @@ private void InitializeComponent() // label13 // this.label13.AutoSize = true; - this.label13.Location = new System.Drawing.Point(10, 168); + this.label13.Location = new System.Drawing.Point(15, 174); this.label13.Name = "label13"; this.label13.Size = new System.Drawing.Size(214, 13); this.label13.TabIndex = 32; @@ -275,7 +208,7 @@ private void InitializeComponent() // checkboxEmuIRQ // this.checkboxEmuIRQ.AutoSize = true; - this.checkboxEmuIRQ.Location = new System.Drawing.Point(197, 146); + this.checkboxEmuIRQ.Location = new System.Drawing.Point(197, 152); this.checkboxEmuIRQ.Name = "checkboxEmuIRQ"; this.checkboxEmuIRQ.Size = new System.Drawing.Size(15, 14); this.checkboxEmuIRQ.TabIndex = 31; @@ -286,7 +219,7 @@ private void InitializeComponent() this.checkboxEmuRESET.AutoSize = true; this.checkboxEmuRESET.Checked = true; this.checkboxEmuRESET.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkboxEmuRESET.Location = new System.Drawing.Point(197, 124); + this.checkboxEmuRESET.Location = new System.Drawing.Point(197, 130); this.checkboxEmuRESET.Name = "checkboxEmuRESET"; this.checkboxEmuRESET.Size = new System.Drawing.Size(15, 14); this.checkboxEmuRESET.TabIndex = 29; @@ -295,7 +228,7 @@ private void InitializeComponent() // checkboxEmuNMI // this.checkboxEmuNMI.AutoSize = true; - this.checkboxEmuNMI.Location = new System.Drawing.Point(197, 102); + this.checkboxEmuNMI.Location = new System.Drawing.Point(197, 108); this.checkboxEmuNMI.Name = "checkboxEmuNMI"; this.checkboxEmuNMI.Size = new System.Drawing.Size(15, 14); this.checkboxEmuNMI.TabIndex = 27; @@ -304,7 +237,7 @@ private void InitializeComponent() // checkboxEmuABORT // this.checkboxEmuABORT.AutoSize = true; - this.checkboxEmuABORT.Location = new System.Drawing.Point(197, 80); + this.checkboxEmuABORT.Location = new System.Drawing.Point(197, 86); this.checkboxEmuABORT.Name = "checkboxEmuABORT"; this.checkboxEmuABORT.Size = new System.Drawing.Size(15, 14); this.checkboxEmuABORT.TabIndex = 25; @@ -313,7 +246,7 @@ private void InitializeComponent() // checkboxEmuBRK // this.checkboxEmuBRK.AutoSize = true; - this.checkboxEmuBRK.Location = new System.Drawing.Point(197, 58); + this.checkboxEmuBRK.Location = new System.Drawing.Point(197, 64); this.checkboxEmuBRK.Name = "checkboxEmuBRK"; this.checkboxEmuBRK.Size = new System.Drawing.Size(15, 14); this.checkboxEmuBRK.TabIndex = 23; @@ -322,7 +255,7 @@ private void InitializeComponent() // checkboxEmuCOP // this.checkboxEmuCOP.AutoSize = true; - this.checkboxEmuCOP.Location = new System.Drawing.Point(197, 36); + this.checkboxEmuCOP.Location = new System.Drawing.Point(197, 42); this.checkboxEmuCOP.Name = "checkboxEmuCOP"; this.checkboxEmuCOP.Size = new System.Drawing.Size(15, 14); this.checkboxEmuCOP.TabIndex = 21; @@ -333,7 +266,7 @@ private void InitializeComponent() this.checkboxNativeIRQ.AutoSize = true; this.checkboxNativeIRQ.Checked = true; this.checkboxNativeIRQ.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkboxNativeIRQ.Location = new System.Drawing.Point(120, 146); + this.checkboxNativeIRQ.Location = new System.Drawing.Point(120, 152); this.checkboxNativeIRQ.Name = "checkboxNativeIRQ"; this.checkboxNativeIRQ.Size = new System.Drawing.Size(15, 14); this.checkboxNativeIRQ.TabIndex = 19; @@ -342,7 +275,7 @@ private void InitializeComponent() // checkboxNativeRESET // this.checkboxNativeRESET.AutoSize = true; - this.checkboxNativeRESET.Location = new System.Drawing.Point(120, 124); + this.checkboxNativeRESET.Location = new System.Drawing.Point(120, 130); this.checkboxNativeRESET.Name = "checkboxNativeRESET"; this.checkboxNativeRESET.Size = new System.Drawing.Size(15, 14); this.checkboxNativeRESET.TabIndex = 17; @@ -353,7 +286,7 @@ private void InitializeComponent() this.checkboxNativeNMI.AutoSize = true; this.checkboxNativeNMI.Checked = true; this.checkboxNativeNMI.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkboxNativeNMI.Location = new System.Drawing.Point(120, 102); + this.checkboxNativeNMI.Location = new System.Drawing.Point(120, 108); this.checkboxNativeNMI.Name = "checkboxNativeNMI"; this.checkboxNativeNMI.Size = new System.Drawing.Size(15, 14); this.checkboxNativeNMI.TabIndex = 15; @@ -362,7 +295,7 @@ private void InitializeComponent() // checkboxNativeABORT // this.checkboxNativeABORT.AutoSize = true; - this.checkboxNativeABORT.Location = new System.Drawing.Point(120, 80); + this.checkboxNativeABORT.Location = new System.Drawing.Point(120, 86); this.checkboxNativeABORT.Name = "checkboxNativeABORT"; this.checkboxNativeABORT.Size = new System.Drawing.Size(15, 14); this.checkboxNativeABORT.TabIndex = 13; @@ -373,7 +306,7 @@ private void InitializeComponent() this.checkboxNativeBRK.AutoSize = true; this.checkboxNativeBRK.Checked = true; this.checkboxNativeBRK.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkboxNativeBRK.Location = new System.Drawing.Point(120, 58); + this.checkboxNativeBRK.Location = new System.Drawing.Point(120, 64); this.checkboxNativeBRK.Name = "checkboxNativeBRK"; this.checkboxNativeBRK.Size = new System.Drawing.Size(15, 14); this.checkboxNativeBRK.TabIndex = 11; @@ -384,7 +317,7 @@ private void InitializeComponent() this.checkboxNativeCOP.AutoSize = true; this.checkboxNativeCOP.Checked = true; this.checkboxNativeCOP.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkboxNativeCOP.Location = new System.Drawing.Point(120, 36); + this.checkboxNativeCOP.Location = new System.Drawing.Point(120, 42); this.checkboxNativeCOP.Name = "checkboxNativeCOP"; this.checkboxNativeCOP.Size = new System.Drawing.Size(15, 14); this.checkboxNativeCOP.TabIndex = 9; @@ -393,7 +326,7 @@ private void InitializeComponent() // label12 // this.label12.AutoSize = true; - this.label12.Location = new System.Drawing.Point(158, 17); + this.label12.Location = new System.Drawing.Point(158, 23); this.label12.Name = "label12"; this.label12.Size = new System.Drawing.Size(53, 13); this.label12.TabIndex = 1; @@ -402,7 +335,7 @@ private void InitializeComponent() // label11 // this.label11.AutoSize = true; - this.label11.Location = new System.Drawing.Point(86, 17); + this.label11.Location = new System.Drawing.Point(86, 23); this.label11.Name = "label11"; this.label11.Size = new System.Drawing.Size(38, 13); this.label11.TabIndex = 0; @@ -410,7 +343,7 @@ private void InitializeComponent() // // textEmuIRQ // - this.textEmuIRQ.Location = new System.Drawing.Point(156, 143); + this.textEmuIRQ.Location = new System.Drawing.Point(156, 149); this.textEmuIRQ.MaxLength = 4; this.textEmuIRQ.Name = "textEmuIRQ"; this.textEmuIRQ.ReadOnly = true; @@ -421,7 +354,7 @@ private void InitializeComponent() // // textEmuRESET // - this.textEmuRESET.Location = new System.Drawing.Point(156, 121); + this.textEmuRESET.Location = new System.Drawing.Point(156, 127); this.textEmuRESET.MaxLength = 4; this.textEmuRESET.Name = "textEmuRESET"; this.textEmuRESET.ReadOnly = true; @@ -432,7 +365,7 @@ private void InitializeComponent() // // textEmuNMI // - this.textEmuNMI.Location = new System.Drawing.Point(156, 99); + this.textEmuNMI.Location = new System.Drawing.Point(156, 105); this.textEmuNMI.MaxLength = 4; this.textEmuNMI.Name = "textEmuNMI"; this.textEmuNMI.ReadOnly = true; @@ -443,7 +376,7 @@ private void InitializeComponent() // // textEmuABORT // - this.textEmuABORT.Location = new System.Drawing.Point(156, 77); + this.textEmuABORT.Location = new System.Drawing.Point(156, 83); this.textEmuABORT.MaxLength = 4; this.textEmuABORT.Name = "textEmuABORT"; this.textEmuABORT.ReadOnly = true; @@ -454,7 +387,7 @@ private void InitializeComponent() // // textEmuBRK // - this.textEmuBRK.Location = new System.Drawing.Point(156, 55); + this.textEmuBRK.Location = new System.Drawing.Point(156, 61); this.textEmuBRK.MaxLength = 4; this.textEmuBRK.Name = "textEmuBRK"; this.textEmuBRK.ReadOnly = true; @@ -465,7 +398,7 @@ private void InitializeComponent() // // textEmuCOP // - this.textEmuCOP.Location = new System.Drawing.Point(156, 33); + this.textEmuCOP.Location = new System.Drawing.Point(156, 39); this.textEmuCOP.MaxLength = 4; this.textEmuCOP.Name = "textEmuCOP"; this.textEmuCOP.ReadOnly = true; @@ -476,7 +409,7 @@ private void InitializeComponent() // // textNativeIRQ // - this.textNativeIRQ.Location = new System.Drawing.Point(79, 143); + this.textNativeIRQ.Location = new System.Drawing.Point(79, 149); this.textNativeIRQ.MaxLength = 4; this.textNativeIRQ.Name = "textNativeIRQ"; this.textNativeIRQ.ReadOnly = true; @@ -487,7 +420,7 @@ private void InitializeComponent() // // textNativeRESET // - this.textNativeRESET.Location = new System.Drawing.Point(79, 121); + this.textNativeRESET.Location = new System.Drawing.Point(79, 127); this.textNativeRESET.MaxLength = 4; this.textNativeRESET.Name = "textNativeRESET"; this.textNativeRESET.ReadOnly = true; @@ -498,7 +431,7 @@ private void InitializeComponent() // // textNativeNMI // - this.textNativeNMI.Location = new System.Drawing.Point(79, 99); + this.textNativeNMI.Location = new System.Drawing.Point(79, 105); this.textNativeNMI.MaxLength = 4; this.textNativeNMI.Name = "textNativeNMI"; this.textNativeNMI.ReadOnly = true; @@ -509,7 +442,7 @@ private void InitializeComponent() // // textNativeABORT // - this.textNativeABORT.Location = new System.Drawing.Point(79, 77); + this.textNativeABORT.Location = new System.Drawing.Point(79, 83); this.textNativeABORT.MaxLength = 4; this.textNativeABORT.Name = "textNativeABORT"; this.textNativeABORT.ReadOnly = true; @@ -520,7 +453,7 @@ private void InitializeComponent() // // textNativeBRK // - this.textNativeBRK.Location = new System.Drawing.Point(79, 55); + this.textNativeBRK.Location = new System.Drawing.Point(79, 61); this.textNativeBRK.MaxLength = 4; this.textNativeBRK.Name = "textNativeBRK"; this.textNativeBRK.ReadOnly = true; @@ -531,7 +464,7 @@ private void InitializeComponent() // // textNativeCOP // - this.textNativeCOP.Location = new System.Drawing.Point(79, 33); + this.textNativeCOP.Location = new System.Drawing.Point(79, 39); this.textNativeCOP.MaxLength = 4; this.textNativeCOP.Name = "textNativeCOP"; this.textNativeCOP.ReadOnly = true; @@ -543,7 +476,7 @@ private void InitializeComponent() // label10 // this.label10.AutoSize = true; - this.label10.Location = new System.Drawing.Point(41, 146); + this.label10.Location = new System.Drawing.Point(41, 152); this.label10.Name = "label10"; this.label10.Size = new System.Drawing.Size(26, 13); this.label10.TabIndex = 7; @@ -552,7 +485,7 @@ private void InitializeComponent() // label9 // this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(25, 124); + this.label9.Location = new System.Drawing.Point(25, 130); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(43, 13); this.label9.TabIndex = 6; @@ -561,7 +494,7 @@ private void InitializeComponent() // label8 // this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(40, 102); + this.label8.Location = new System.Drawing.Point(40, 108); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(27, 13); this.label8.TabIndex = 5; @@ -570,7 +503,7 @@ private void InitializeComponent() // label7 // this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(24, 80); + this.label7.Location = new System.Drawing.Point(24, 86); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(44, 13); this.label7.TabIndex = 4; @@ -579,7 +512,7 @@ private void InitializeComponent() // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(38, 58); + this.label2.Location = new System.Drawing.Point(38, 64); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(29, 13); this.label2.TabIndex = 3; @@ -588,7 +521,7 @@ private void InitializeComponent() // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(39, 36); + this.label1.Location = new System.Drawing.Point(39, 42); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(29, 13); this.label1.TabIndex = 2; @@ -599,19 +532,90 @@ private void InitializeComponent() this.checkHeader.AutoSize = true; this.checkHeader.Checked = true; this.checkHeader.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkHeader.Location = new System.Drawing.Point(12, 365); + this.checkHeader.Location = new System.Drawing.Point(12, 371); this.checkHeader.Name = "checkHeader"; this.checkHeader.Size = new System.Drawing.Size(237, 17); this.checkHeader.TabIndex = 6; this.checkHeader.Text = "Auto generate flags for Internal ROM Header"; this.checkHeader.UseVisualStyleBackColor = true; + this.checkHeader.CheckedChanged += new System.EventHandler(this.checkHeader_CheckedChanged); + // + // label14 // - // ImportROMDialog + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(9, 7); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(112, 13); + this.label14.TabIndex = 9; + this.label14.Text = "Detected Rom Mode: "; // + // romspeed + // + this.romspeed.AutoSize = true; + this.romspeed.Location = new System.Drawing.Point(103, 50); + this.romspeed.Name = "romspeed"; + this.romspeed.Size = new System.Drawing.Size(55, 13); + this.romspeed.TabIndex = 3; + this.romspeed.Text = "SlowROM"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(34, 72); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(66, 13); + this.label5.TabIndex = 4; + this.label5.Text = "ROM Name:"; + // + // romtitle + // + this.romtitle.AutoSize = true; + this.romtitle.Location = new System.Drawing.Point(103, 72); + this.romtitle.Name = "romtitle"; + this.romtitle.Size = new System.Drawing.Size(27, 13); + this.romtitle.TabIndex = 5; + this.romtitle.Text = "Title"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(31, 50); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(69, 13); + this.label4.TabIndex = 2; + this.label4.Text = "ROM Speed:"; + // + // cmbRomMapMode + // + this.cmbRomMapMode.FormattingEnabled = true; + this.cmbRomMapMode.Location = new System.Drawing.Point(102, 24); + this.cmbRomMapMode.Name = "cmbRomMapMode"; + this.cmbRomMapMode.Size = new System.Drawing.Size(142, 21); + this.cmbRomMapMode.TabIndex = 6; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.cmbRomMapMode); + this.groupBox1.Controls.Add(sourceLabel); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.romtitle); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.romspeed); + this.groupBox1.Location = new System.Drawing.Point(12, 80); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(246, 89); + this.groupBox1.TabIndex = 4; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "ROM Information"; + // + // ImportRomDialog + // + this.AcceptButton = this.okay; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancel; - this.ClientSize = new System.Drawing.Size(261, 424); + this.ClientSize = new System.Drawing.Size(272, 424); + this.Controls.Add(this.label14); this.Controls.Add(this.checkHeader); this.Controls.Add(this.groupBox2); this.Controls.Add(this.groupBox1); @@ -622,16 +626,17 @@ private void InitializeComponent() this.Controls.Add(this.checkData); this.Controls.Add(this.detectMessage); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; - this.Name = "ImportROMDialog"; + this.Name = "ImportRomDialog"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "New Project"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ImportRomDialog_FormClosing); this.Load += new System.EventHandler(this.ImportROMDialog_Load); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); this.groupBox2.ResumeLayout(false); this.groupBox2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -641,17 +646,10 @@ private void InitializeComponent() private System.Windows.Forms.Label detectMessage; private System.Windows.Forms.Label checkData; - private System.Windows.Forms.ComboBox comboBox1; - private System.Windows.Forms.Label mapmode; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label5; private System.Windows.Forms.Button cancel; private System.Windows.Forms.Button okay; - private System.Windows.Forms.Label romspeed; - private System.Windows.Forms.Label romtitle; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label6; - private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.Label label13; private System.Windows.Forms.CheckBox checkboxEmuIRQ; @@ -687,5 +685,12 @@ private void InitializeComponent() private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label1; private System.Windows.Forms.CheckBox checkHeader; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Label romspeed; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label romtitle; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox cmbRomMapMode; + private System.Windows.Forms.GroupBox groupBox1; } } \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/ImportROMDialog.cs b/DiztinGUIsh/window/dialog/ImportROMDialog.cs index 2133591b..2c4a653c 100644 --- a/DiztinGUIsh/window/dialog/ImportROMDialog.cs +++ b/DiztinGUIsh/window/dialog/ImportROMDialog.cs @@ -1,250 +1,221 @@ using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Diagnostics; using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.serialization; +using Diz.Core.util; -namespace DiztinGUIsh +namespace DiztinGUIsh.window.dialog { - public partial class ImportROMDialog : Form + public partial class ImportRomDialog : Form, IImportRomDialogView { - private Data.ROMMapMode mode; - private Data.ROMSpeed speed; - private string title; - private byte[] data; - private int offset; - private string[,] vectorNames = new string[2, 6] - { - { "Native_COP", "Native_BRK", "Native_ABORT", "Native_NMI", "Native_RESET", "Native_IRQ"}, - { "Emulation_COP", "Emulation_Unknown", "Emulation_ABORT", "Emulation_NMI", "Emulation_RESET", "Emulation_IRQBRK"} + // NOTE: all this could be converted to use databinding and be easier to deal with, but, + // probably more work than its worth. this is fine, if a bit manual. it's unlikely to ever need to change. + private CheckBox[,] GetVectorsCheckboxes() => new[,] + { + {checkboxNativeCOP, checkboxNativeBRK, checkboxNativeABORT, checkboxNativeNMI, checkboxNativeRESET, checkboxNativeIRQ}, + {checkboxEmuCOP, checkboxEmuBRK, checkboxEmuABORT, checkboxEmuNMI, checkboxEmuRESET, checkboxEmuIRQ}, + }; + private TextBox[,] GetVectorsTextBoxes() => new[,] + { + {textNativeCOP, textNativeBRK, textNativeABORT, textNativeNMI, textNativeRESET, textNativeIRQ}, + {textEmuCOP, textEmuBRK, textEmuABORT, textEmuNMI, textEmuRESET, textEmuIRQ}, }; - private TextBox[,] vectors; - private CheckBox[,] checkboxes; + private readonly TextBox[,] vectors; + private readonly CheckBox[,] checkboxes; - public ImportROMDialog(byte [] rom) + private ImportRomDialogController controller; + public ImportRomDialogController Controller { - InitializeComponent(); - vectors = new TextBox[2, 6] - { - { textNativeCOP, textNativeBRK, textNativeABORT, textNativeNMI, textNativeRESET, textNativeIRQ }, - { textEmuCOP, textEmuBRK, textEmuABORT, textEmuNMI, textEmuRESET, textEmuIRQ }, - }; - checkboxes = new CheckBox[2, 6] + get => controller; + set { - { checkboxNativeCOP, checkboxNativeBRK, checkboxNativeABORT, checkboxNativeNMI, checkboxNativeRESET, checkboxNativeIRQ }, - { checkboxEmuCOP, checkboxEmuBRK, checkboxEmuABORT, checkboxEmuNMI, checkboxEmuRESET, checkboxEmuIRQ }, - }; - data = rom; - mode = DetectROMMapMode(); - UpdateOffsetAndSpeed(); + if (controller != null) + controller.SettingsCreated -= Controller_SettingsCreated; + + controller = value; + + if (controller != null) + controller.SettingsCreated += Controller_SettingsCreated; + } } + public ImportRomSettings ImportSettings => Controller?.ImportSettings; - public Dictionary GetGeneratedLabels() + public ImportRomDialog() { - var labels = new Dictionary(); - - for (int i = 0; i < checkboxes.GetLength(0); i++) - { - for (int j = 0; j < checkboxes.GetLength(1); j++) - { - if (checkboxes[i, j].Checked) - { - int index = offset + 15 + 0x10 * i + 2 * j; - int val = data[index] + (data[index + 1] << 8); - int pc = Util.ConvertSNEStoPC(val); - if (pc >= 0 && pc < data.Length && !labels.ContainsKey(val)) - labels.Add(val, new Data.AliasInfo() {name = vectorNames[i, j]}); - } - } - } + InitializeComponent(); - return labels; + vectors = GetVectorsTextBoxes(); + checkboxes = GetVectorsCheckboxes(); } - public Dictionary GetHeaderFlags() + private void DataBind() { - Dictionary flags = new Dictionary(); + Debug.Assert(ImportSettings != null); + GuiUtil.BindListControlToEnum(cmbRomMapMode, ImportSettings, "ROMMapMode"); - if (checkHeader.Checked) - { - for (int i = 0; i < 0x15; i++) flags.Add(offset - 0x15 + i, Data.FlagType.Text); - for (int i = 0; i < 7; i++) flags.Add(offset + i, Data.FlagType.Data8Bit); - for (int i = 0; i < 4; i++) flags.Add(offset + 7 + i, Data.FlagType.Data16Bit); - for (int i = 0; i < 0x20; i++) flags.Add(offset + 11 + i, Data.FlagType.Pointer16Bit); + checkHeader.Checked = Controller.ShouldCheckHeader; // todo: databind this instead. + ImportSettings.PropertyChanged += ImportSettingsOnPropertyChanged; + } - if (data[offset - 1] == 0) - { - flags.Remove(offset - 1); - flags.Add(offset - 1, Data.FlagType.Data8Bit); - for (int i = 0; i < 0x10; i++) flags.Add(offset - 0x25 + i, Data.FlagType.Data8Bit); - } - else if (data[offset + 5] == 0x33) - { - for (int i = 0; i < 6; i++) flags.Add(offset - 0x25 + i, Data.FlagType.Text); - for (int i = 0; i < 10; i++) flags.Add(offset - 0x1F + i, Data.FlagType.Data8Bit); - } - } + public bool ShowAndWaitForUserToConfirmSettings() + { + return ShowDialog() == DialogResult.OK; + } - return flags; + private void Controller_SettingsCreated() + { + DataBind(); + RefreshUi(); } - private Data.ROMMapMode DetectROMMapMode() + private void ImportSettingsOnPropertyChanged(object sender, PropertyChangedEventArgs e) { - if ((data[Data.LOROM_SETTING_OFFSET] & 0xEF) == 0x23) - { - if (data.Length > 0x400000) - { - detectMessage.Text = "ROM Map Mode Detected: SA-1 ROM (FuSoYa's 8MB mapper)"; - comboBox1.SelectedIndex = 4; - return Data.ROMMapMode.ExSA1ROM; - } - else - { - detectMessage.Text = "ROM Map Mode Detected: SA-1 ROM"; - comboBox1.SelectedIndex = 3; - return Data.ROMMapMode.SA1ROM; - } - } - else if ((data[Data.LOROM_SETTING_OFFSET] & 0xEC) == 0x20) - { - if ((data[Data.LOROM_SETTING_OFFSET + 1] & 0xF0) == 0x10) { - detectMessage.Text = "ROM Map Mode Detected: SuperFX"; - comboBox1.SelectedIndex = 5; - return Data.ROMMapMode.SuperFX; - } else { - detectMessage.Text = "ROM Map Mode Detected: LoROM"; - comboBox1.SelectedIndex = 0; - return Data.ROMMapMode.LoROM; - } - } - else if (data.Length >= 0x10000 && (data[Data.HIROM_SETTING_OFFSET] & 0xEF) == 0x21) - { - detectMessage.Text = "ROM Map Mode Detected: HiROM"; - comboBox1.SelectedIndex = 1; - return Data.ROMMapMode.HiROM; - } - else if (data.Length >= 0x10000 && (data[Data.HIROM_SETTING_OFFSET] & 0xE7) == 0x22) - { - detectMessage.Text = "ROM Map Mode Detected: Super MMC"; - comboBox1.SelectedIndex = 2; - return Data.ROMMapMode.SuperMMC; - } - else if (data.Length >= 0x410000 && (data[Data.EXHIROM_SETTING_OFFSET] & 0xEF) == 0x25) - { - detectMessage.Text = "ROM Map Mode Detected: ExHiROM"; - comboBox1.SelectedIndex = 6; - return Data.ROMMapMode.ExHiROM; - } - else + RefreshUi(); + } + + private void RefreshUi() + { + UpdateOffsetAndSpeed(); + UpdateTextboxes(); + UpdateOkayButtonEnabled(); + detectMessage.Text = GetDetectionMessage(); + UpdateVectorTable(); + } + + private string GetDetectionMessage() => + Controller.DetectedMapMode.HasValue ? Util.GetEnumDescription(ImportSettings.RomMapMode) : "Couldn't auto detect ROM Map Mode!"; + + public void UpdateVectorTable() + { + // it'd probably be easier to just setup a lookup table for this stuff + // it's probably also better to databind all this, but might be complex. this is OK for now. + + string[,] vectorNames = { + {"Native_COP", "Native_BRK", "Native_ABORT", "Native_NMI", "Native_RESET", "Native_IRQ"}, + {"Emulation_COP", "Emulation_Unknown", "Emulation_ABORT", "Emulation_NMI", "Emulation_RESET", "Emulation_IRQBRK"} + }; + + Controller.VectorTableEntriesEnabled.Clear(); + Debug.Assert(checkboxes.GetLength(0) == vectorNames.GetLength(0)); + Debug.Assert(checkboxes.GetLength(1) == vectorNames.GetLength(1)); + for (var i = 0; i < checkboxes.GetLength(0); i++) { - detectMessage.Text = "Couldn't auto detect ROM Map Mode!"; - if (data.Length > 0x40000) - { - comboBox1.SelectedIndex = 7; - return Data.ROMMapMode.ExLoROM; - } else + for (var j = 0; j < checkboxes.GetLength(1); j++) { - comboBox1.SelectedIndex = 0; - return Data.ROMMapMode.LoROM; + Controller.VectorTableEntriesEnabled.Add(vectorNames[i, j], checkboxes[i,j].Checked); } } } - public Data.ROMMapMode GetROMMapMode() + private void UpdateOffsetAndSpeed() { - return mode; + Controller.RomSettingsOffset = RomUtil.GetRomSettingOffset(ImportSettings.RomMapMode); + ImportSettings.RomSpeed = RomUtil.GetRomSpeed(Controller.RomSettingsOffset, ImportSettings.RomBytes); } - public Data.ROMSpeed GetROMSpeed() + private void UpdateOkayButtonEnabled() { - return speed; + okay.Enabled = ImportSettings.RomSpeed != RomSpeed.Unknown; } - private void UpdateOffsetAndSpeed() + private void UpdateTextboxes() { - offset = Data.GetRomSettingOffset(mode); - if (offset >= data.Length) + if (IsProbablyValidDetection()) { - speed = Data.ROMSpeed.Unknown; - okay.Enabled = false; - } else - { - okay.Enabled = true; - speed = (data[offset] & 0x10) != 0 ? Data.ROMSpeed.FastROM : Data.ROMSpeed.SlowROM; + try + { + UpdateDetectedValues(); + return; + } + catch (Exception) + { + // ignored + } } + + SetDefaultsIfDetectionFailed(); } - private void UpdateTextboxes() - { - if (speed == Data.ROMSpeed.Unknown) - { - romspeed.Text = "????"; - romtitle.Text = "?????????????????????"; - for (int i = 0; i < vectors.GetLength(0); i++) for (int j = 0; j < vectors.GetLength(1); j++) vectors[i, j].Text = "????"; - } else - { - if (speed == Data.ROMSpeed.SlowROM) romspeed.Text = "SlowROM"; - else romspeed.Text = "FastROM"; + private bool IsOffsetInRange(int offset) => + offset > 0 && offset <= ImportSettings.RomBytes.Length; - title = ""; - for (int i = 0; i < 0x15; i++) title += (char)data[offset - 0x15 + i]; - romtitle.Text = title; + private bool IsProbablyValidDetection() => + ImportSettings.RomSpeed != RomSpeed.Unknown && IsOffsetInRange(Controller.RomSettingsOffset); - for (int i = 0; i < vectors.GetLength(0); i++) + private void SetDefaultsIfDetectionFailed() + { + romspeed.Text = "????"; + romtitle.Text = "?????????????????????"; + for (var i = 0; i < vectors.GetLength(0); i++) + for (var j = 0; j < vectors.GetLength(1); j++) + vectors[i, j].Text = "????"; + } + + private void UpdateDetectedValues() + { + // caution: things can go wrong here if we didn't guess settings correctly, + // you usually want to call this function with a try/catch around it. + for (var i = 0; i < vectors.GetLength(0); i++) + { + for (var j = 0; j < vectors.GetLength(1); j++) { - for (int j = 0; j < vectors.GetLength(1); j++) + var index = Controller.RomSettingsOffset + 15 + 0x10 * i + 2 * j; + var val = ImportSettings.RomBytes[index] + (ImportSettings.RomBytes[index + 1] << 8); + vectors[i, j].Text = Util.NumberToBaseString(val, Util.NumberBase.Hexadecimal, 4); + + if (val < 0x8000) { - int index = offset + 15 + 0x10 * i + 2 * j; - int val = data[index] + (data[index + 1] << 8); - vectors[i, j].Text = Util.NumberToBaseString(val, Util.NumberBase.Hexadecimal, 4); - - if (val < 0x8000) - { - checkboxes[i, j].Checked = false; - checkboxes[i, j].Enabled = false; - } else - { - checkboxes[i, j].Enabled = true; - } + checkboxes[i, j].Checked = false; + checkboxes[i, j].Enabled = false; + } + else + { + checkboxes[i, j].Enabled = true; } } } + var romSpeedStr = Util.GetEnumDescription(ImportSettings.RomSpeed); + var romTitleName = RomUtil.GetRomTitleName(ImportSettings.RomBytes, Controller.RomSettingsOffset); + + romspeed.Text = romSpeedStr; + romtitle.Text = romTitleName; } - private void ImportROMDialog_Load(object sender, EventArgs e) + private void ImportROMDialog_Load(object sender, EventArgs e) => RefreshUi(); + + private void okay_Click(object sender, EventArgs e) { - UpdateTextboxes(); + if (!Controller.Submit()) + return; + + SetFinished(); } - private void okay_Click(object sender, EventArgs e) + public bool PromptToConfirmAction(string msg) { - this.DialogResult = DialogResult.OK; + return GuiUtil.PromptToConfirmAction("Warning", msg, () => true); } - private void cancel_Click(object sender, EventArgs e) + private void SetFinished() { - this.Close(); + DialogResult = DialogResult.OK; } - private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + private void cancel_Click(object sender, EventArgs e) => Close(); + + private void checkHeader_CheckedChanged(object sender, EventArgs e) { - switch (comboBox1.SelectedIndex) - { - case 0: mode = Data.ROMMapMode.LoROM; break; - case 1: mode = Data.ROMMapMode.HiROM; break; - case 2: mode = Data.ROMMapMode.SuperMMC; break; - case 3: mode = Data.ROMMapMode.SA1ROM; break; - case 4: mode = Data.ROMMapMode.ExSA1ROM; break; - case 5: mode = Data.ROMMapMode.SuperFX; break; - case 6: mode = Data.ROMMapMode.ExHiROM; break; - case 7: mode = Data.ROMMapMode.ExLoROM; break; - } - UpdateOffsetAndSpeed(); - UpdateTextboxes(); + Controller.ShouldCheckHeader = checkHeader.Checked; // todo: databind this instead. + } + + private void ImportRomDialog_FormClosing(object sender, FormClosingEventArgs e) + { + if (ImportSettings != null) + ImportSettings.PropertyChanged -= ImportSettingsOnPropertyChanged; + + Controller = null; } } -} +} \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/ImportROMDialog.resx b/DiztinGUIsh/window/dialog/ImportROMDialog.resx index 1af7de15..83afe152 100644 --- a/DiztinGUIsh/window/dialog/ImportROMDialog.resx +++ b/DiztinGUIsh/window/dialog/ImportROMDialog.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + False + \ No newline at end of file diff --git a/DiztinGUIsh/window/dialog/ImportROMDialogController.cs b/DiztinGUIsh/window/dialog/ImportROMDialogController.cs new file mode 100644 index 00000000..1767c4a3 --- /dev/null +++ b/DiztinGUIsh/window/dialog/ImportROMDialogController.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using Diz.Core.model; +using Diz.Core.serialization; +using Diz.Core.util; + +namespace DiztinGUIsh.window.dialog +{ + public interface IImportRomDialogView + { + bool PromptToConfirmAction(string msg); + bool ShowAndWaitForUserToConfirmSettings(); + ImportRomDialogController Controller { get; set; } + } + + public class ImportRomDialogController + { + public IImportRomDialogView View { get; set; } + public ImportRomSettings ImportSettings { get; protected set; } + public RomMapMode? DetectedMapMode { get; protected set; } + + public int RomSettingsOffset { get; set; }= -1; + public bool ShouldCheckHeader { get; set; } = true; + public Dictionary VectorTableEntriesEnabled { get; set; } = new Dictionary(); + + public delegate void SettingsCreatedEvent(); + public event SettingsCreatedEvent SettingsCreated; + + public ImportRomSettings PromptUserForRomSettings(string romFilename) + { + CreateSettingsFromRom(romFilename); + + var shouldContinue = View.ShowAndWaitForUserToConfirmSettings(); + if (!shouldContinue) + return null; + + ImportSettings.InitialLabels = GenerateVectorLabels(); + ImportSettings.InitialHeaderFlags = GenerateHeaderFlags(); + + return ImportSettings; + } + + public void CreateSettingsFromRom(string filename) + { + var romBytes = RomUtil.ReadAllRomBytesFromFile(filename); + ImportSettings = new ImportRomSettings + { + RomFilename = filename, + RomBytes = romBytes, + RomMapMode = RomUtil.DetectRomMapMode(romBytes, out var detectedMapModeSuccess) + }; + + if (detectedMapModeSuccess) + DetectedMapMode = ImportSettings.RomMapMode; + + OnSettingsCreated(); + } + + protected virtual void OnSettingsCreated() + { + SettingsCreated?.Invoke(); + } + + private bool Warn(string msg) + { + return View.PromptToConfirmAction(msg + + "\nIf you proceed with this import, imported data might be wrong.\n" + + "Proceed anyway?\n\n (Experts only, otherwise say No)"); + } + + public bool Submit() + { + if (!DetectedMapMode.HasValue) + { + if (!Warn("ROM Map type couldn't be detected.")) + return false; + } + else if (DetectedMapMode.Value != ImportSettings.RomMapMode) + { + if (!Warn("The ROM map type selected is different than what was detected.")) + return false; + } + + return true; + } + + private Dictionary GenerateVectorLabels() => + RomUtil.GenerateVectorLabels( + VectorTableEntriesEnabled, RomSettingsOffset, ImportSettings.RomBytes, ImportSettings.RomMapMode); + + public Dictionary GenerateHeaderFlags() + { + var flags = new Dictionary(); + + if (ShouldCheckHeader) + RomUtil.GenerateHeaderFlags(RomSettingsOffset, flags, ImportSettings.RomBytes); + + return flags; + } + } +} diff --git a/DiztinGUIsh/window/dialog/InOutPointChecker.Designer.cs b/DiztinGUIsh/window/dialog/InOutPointChecker.Designer.cs index 9aabf953..ac611b19 100644 --- a/DiztinGUIsh/window/dialog/InOutPointChecker.Designer.cs +++ b/DiztinGUIsh/window/dialog/InOutPointChecker.Designer.cs @@ -66,6 +66,7 @@ private void InitializeComponent() // // InOutPointChecker // + this.AcceptButton = this.rescan; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancel; diff --git a/DiztinGUIsh/window/dialog/InOutPointChecker.cs b/DiztinGUIsh/window/dialog/InOutPointChecker.cs index 20e636bd..79484e8d 100644 --- a/DiztinGUIsh/window/dialog/InOutPointChecker.cs +++ b/DiztinGUIsh/window/dialog/InOutPointChecker.cs @@ -1,11 +1,4 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace DiztinGUIsh diff --git a/DiztinGUIsh/window/dialog/MarkManyDialog.Designer.cs b/DiztinGUIsh/window/dialog/MarkManyDialog.Designer.cs index 71ca9783..bcfdaf76 100644 --- a/DiztinGUIsh/window/dialog/MarkManyDialog.Designer.cs +++ b/DiztinGUIsh/window/dialog/MarkManyDialog.Designer.cs @@ -1,4 +1,4 @@ -namespace DiztinGUIsh +namespace DiztinGUIsh.window.dialog { partial class MarkManyDialog { @@ -284,8 +284,10 @@ private void InitializeComponent() // // MarkManyDialog // + this.AcceptButton = this.okay; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancel; this.ClientSize = new System.Drawing.Size(299, 206); this.Controls.Add(this.radioDec); this.Controls.Add(this.radioHex); diff --git a/DiztinGUIsh/window/dialog/MarkManyDialog.cs b/DiztinGUIsh/window/dialog/MarkManyDialog.cs index 63b426f7..a78e6550 100644 --- a/DiztinGUIsh/window/dialog/MarkManyDialog.cs +++ b/DiztinGUIsh/window/dialog/MarkManyDialog.cs @@ -1,35 +1,39 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; -namespace DiztinGUIsh +namespace DiztinGUIsh.window.dialog { public partial class MarkManyDialog : Form { - private int start, end, count, value; + public int Start { get; private set; } + public int End { get; private set; } + public int Count { get; private set; } + + private int value; - public MarkManyDialog(int offset, int column) + private readonly Data data; + + public MarkManyDialog(int offset, int column, Data data) { InitializeComponent(); - switch (column) + this.data = data; + + property.SelectedIndex = column switch { - case 8: property.SelectedIndex = 1; break; - case 9: property.SelectedIndex = 2; break; - case 10: property.SelectedIndex = 3; break; - case 11: property.SelectedIndex = 4; break; - default: property.SelectedIndex = 0; break; - } - start = offset; - int rest = Data.GetROMSize() - start; - count = rest < 0x10 ? rest : 0x10; - end = start + count; + 8 => 1, + 9 => 2, + 10 => 3, + 11 => 4, + _ => 0 + }; + + Start = offset; + var rest = this.data.GetRomSize() - Start; + Count = rest < 0x10 ? rest : 0x10; + End = Start + Count; flagCombo.SelectedIndex = 3; archCombo.SelectedIndex = 0; @@ -38,61 +42,55 @@ public MarkManyDialog(int offset, int column) UpdateGroup(); UpdateText(null); } + + public int Property => property.SelectedIndex; - public int GetOffset() - { - return start; - } - - public int GetCount() - { - return count; - } - - public int GetProperty() - { - return property.SelectedIndex; - } - - public object GetValue() - { - switch (property.SelectedIndex) + public object Value { + get { - case 0: - switch (flagCombo.SelectedIndex) - { - case 0: return Data.FlagType.Unreached; - case 1: return Data.FlagType.Opcode; - case 2: return Data.FlagType.Operand; - case 3: return Data.FlagType.Data8Bit; - case 4: return Data.FlagType.Graphics; - case 5: return Data.FlagType.Music; - case 6: return Data.FlagType.Empty; - case 7: return Data.FlagType.Data16Bit; - case 8: return Data.FlagType.Pointer16Bit; - case 9: return Data.FlagType.Data24Bit; - case 10: return Data.FlagType.Pointer24Bit; - case 11: return Data.FlagType.Data32Bit; - case 12: return Data.FlagType.Pointer32Bit; - case 13: return Data.FlagType.Text; - } - break; - case 1: - case 2: - return value; - case 3: - case 4: - return mxCombo.SelectedIndex != 0; - case 5: - switch (archCombo.SelectedIndex) - { - case 0: return Data.Architechture.CPU65C816; - case 1: return Data.Architechture.APUSPC700; - case 2: return Data.Architechture.GPUSuperFX; - } - break; + switch (property.SelectedIndex) + { + case 0: + switch (flagCombo.SelectedIndex) + { + case 0: return Data.FlagType.Unreached; + case 1: return Data.FlagType.Opcode; + case 2: return Data.FlagType.Operand; + case 3: return Data.FlagType.Data8Bit; + case 4: return Data.FlagType.Graphics; + case 5: return Data.FlagType.Music; + case 6: return Data.FlagType.Empty; + case 7: return Data.FlagType.Data16Bit; + case 8: return Data.FlagType.Pointer16Bit; + case 9: return Data.FlagType.Data24Bit; + case 10: return Data.FlagType.Pointer24Bit; + case 11: return Data.FlagType.Data32Bit; + case 12: return Data.FlagType.Pointer32Bit; + case 13: return Data.FlagType.Text; + } + + break; + case 1: + case 2: + return value; + case 3: + case 4: + return mxCombo.SelectedIndex != 0; + case 5: + switch (archCombo.SelectedIndex) + { + case 0: return Data.Architecture.Cpu65C816; + case 1: return Data.Architecture.Apuspc700; + case 2: return Data.Architecture.GpuSuperFx; + } + + break; + default: + return 0; + } + + return 0; } - return 0; } private void UpdateGroup() @@ -102,81 +100,43 @@ private void UpdateGroup() mxCombo.Visible = (property.SelectedIndex == 3 || property.SelectedIndex == 4); archCombo.Visible = (property.SelectedIndex == 5); regValue.MaxLength = (property.SelectedIndex == 1 ? 3 : 5); - value = property.SelectedIndex == 1 ? Data.GetDataBank(start) : Data.GetDirectPage(start); + value = property.SelectedIndex == 1 ? data.GetDataBank(Start) : data.GetDirectPage(Start); } - private bool updatingText = false; + private bool updatingText; private void UpdateText(TextBox selected) { Util.NumberBase noBase = radioDec.Checked ? Util.NumberBase.Decimal : Util.NumberBase.Hexadecimal; int digits = noBase == Util.NumberBase.Hexadecimal && radioROM.Checked ? 6 : 0; - int size = Data.GetROMSize(); + int size = data.GetRomSize(); int maxValue = property.SelectedIndex == 1 ? 0x100 : 0x10000; - if (start < 0) start = 0; - if (end >= size) end = size - 1; - count = end - start; - if (count < 0) count = 0; + if (Start < 0) Start = 0; + if (End >= size) End = size - 1; + Count = End - Start; + if (Count < 0) Count = 0; if (value < 0) value = 0; if (value >= maxValue) value = maxValue - 1; updatingText = true; - if (selected != textStart) textStart.Text = Util.NumberToBaseString(radioROM.Checked ? Util.ConvertPCtoSNES(start) : start, noBase, digits); - if (selected != textEnd) textEnd.Text = Util.NumberToBaseString(radioROM.Checked ? Util.ConvertPCtoSNES(end) : end, noBase, digits); - if (selected != textCount) textCount.Text = Util.NumberToBaseString(count, noBase, 0); + if (selected != textStart) textStart.Text = Util.NumberToBaseString(radioROM.Checked ? data.ConvertPCtoSnes(Start) : Start, noBase, digits); + if (selected != textEnd) textEnd.Text = Util.NumberToBaseString(radioROM.Checked ? data.ConvertPCtoSnes(End) : End, noBase, digits); + if (selected != textCount) textCount.Text = Util.NumberToBaseString(Count, noBase, 0); if (selected != regValue) regValue.Text = Util.NumberToBaseString(value, noBase, 0); updatingText = false; } - - private void textCount_TextChanged(object sender, EventArgs e) - { - if (!updatingText) - { - updatingText = true; - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; - - int result = 0; - if (int.TryParse(textCount.Text, style, null, out result)) - { - count = result; - end = start + count; - } - - UpdateText(textCount); - } - } - - private void textEnd_TextChanged(object sender, EventArgs e) - { - if (!updatingText) - { - updatingText = true; - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; - - int result = 0; - if (int.TryParse(textEnd.Text, style, null, out result)) - { - if (radioROM.Checked) result = Util.ConvertSNEStoPC(result); - end = result; - count = end - start; - } - - UpdateText(textEnd); - } - } - + private void property_SelectedIndexChanged(object sender, EventArgs e) { UpdateGroup(); } - + private void regValue_TextChanged(object sender, EventArgs e) { - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; + var style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; - int result = 0; - if (int.TryParse(regValue.Text, style, null, out result)) + if (int.TryParse(regValue.Text, style, null, out var result)) { value = result; } @@ -189,26 +149,54 @@ private void okay_Click(object sender, EventArgs e) private void cancel_Click(object sender, EventArgs e) { - this.Close(); + Close(); + } + + private void textCount_TextChanged(object sender, EventArgs e) + { + OnTextChanged(textCount, value => + { + Count = value; + End = Start + Count; + }); + } + + private void textEnd_TextChanged(object sender, EventArgs e) + { + OnTextChanged(textEnd, value => + { + if (radioROM.Checked) + value = data.ConvertSnesToPc(value); + + End = value; + Count = End - Start; + }); } private void textStart_TextChanged(object sender, EventArgs e) { - if (!updatingText) + OnTextChanged(textStart, value => { - updatingText = true; - NumberStyles style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; + if (radioROM.Checked) + value = data.ConvertSnesToPc(value); - int result = 0; - if (int.TryParse(textStart.Text, style, null, out result)) - { - if (radioROM.Checked) result = Util.ConvertSNEStoPC(result); - start = result; - count = end - start; - } + Start = value; + Count = End - Start; + }); + } + + private void OnTextChanged(TextBox textBox, Action OnResult) + { + if (updatingText) + return; - UpdateText(textStart); - } + updatingText = true; + var style = radioDec.Checked ? NumberStyles.Number : NumberStyles.HexNumber; + + if (int.TryParse(textBox.Text, style, null, out var result)) + OnResult(result); + + UpdateText(textBox); } private void radioHex_CheckedChanged(object sender, EventArgs e) diff --git a/DiztinGUIsh/window/dialog/MisalignmentChecker.cs b/DiztinGUIsh/window/dialog/MisalignmentChecker.cs index 7a403378..0b062b27 100644 --- a/DiztinGUIsh/window/dialog/MisalignmentChecker.cs +++ b/DiztinGUIsh/window/dialog/MisalignmentChecker.cs @@ -1,19 +1,16 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; namespace DiztinGUIsh { public partial class MisalignmentChecker : Form { - public MisalignmentChecker() + private Data Data { get; set; } + public MisalignmentChecker(Data data) { + Data = data; InitializeComponent(); } @@ -27,30 +24,24 @@ private void buttonScan_Click(object sender, EventArgs e) textLog.Text = ""; int found = 0, offset = 0; - while (found < 500 && offset < Data.GetROMSize()) + while (found < 500 && offset < Data.GetRomSize()) { Data.FlagType flag = Data.GetFlag(offset), check = flag == Data.FlagType.Opcode ? Data.FlagType.Operand : flag; - int step = flag == Data.FlagType.Opcode ? Manager.GetInstructionLength(offset) : Util.TypeStepSize(flag); + var step = flag == Data.FlagType.Opcode ? Data.GetInstructionLength(offset) : RomUtil.GetByteLengthForFlag(flag); if (flag == Data.FlagType.Operand) { found++; - textLog.Text += string.Format("{0} (0x{1}): Operand without Opcode\r\n", - Util.NumberToBaseString(Util.ConvertPCtoSNES(offset), Util.NumberBase.Hexadecimal, 6, true), - Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 0)); + textLog.Text += + $"{Util.NumberToBaseString(Data.ConvertPCtoSnes(offset), Util.NumberBase.Hexadecimal, 6, true)} (0x{Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 0)}): Operand without Opcode\r\n"; } else if (step > 1) { - for (int i = 1; i < step; i++) + for (var i = 1; i < step; i++) { - if (Data.GetFlag(offset + i) != check) - { - found++; - textLog.Text += string.Format("{0} (0x{1}): {2} is not {3}\r\n", - Util.NumberToBaseString(Util.ConvertPCtoSNES(offset + i), Util.NumberBase.Hexadecimal, 6, true), - Util.NumberToBaseString(offset + i, Util.NumberBase.Hexadecimal, 0), - Util.TypeToString(Data.GetFlag(offset + i)), - Util.TypeToString(check)); - } + if (Data.GetFlag(offset + i) == check) continue; + found++; + textLog.Text += + $"{Util.NumberToBaseString(Data.ConvertPCtoSnes(offset + i), Util.NumberBase.Hexadecimal, 6, true)} (0x{Util.NumberToBaseString(offset + i, Util.NumberBase.Hexadecimal, 0)}): {Util.GetEnumDescription(Data.GetFlag(offset + i))} is not {Util.GetEnumDescription(check)}\r\n"; } } diff --git a/DiztinGUIsh/window/dialog/ProgressDialog.Designer.cs b/DiztinGUIsh/window/dialog/ProgressDialog.Designer.cs new file mode 100644 index 00000000..11420369 --- /dev/null +++ b/DiztinGUIsh/window/dialog/ProgressDialog.Designer.cs @@ -0,0 +1,106 @@ +namespace DiztinGUIsh.window.dialog +{ + partial class ProgressDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + 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.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(11, 264); + this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(40, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Status:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(55, 264); + this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(73, 13); + this.label2.TabIndex = 1; + this.label2.Text = "[progress text]"; + // + // progressBar1 + // + this.progressBar1.Location = new System.Drawing.Point(11, 286); + this.progressBar1.Margin = new System.Windows.Forms.Padding(2); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(255, 19); + this.progressBar1.TabIndex = 2; + // + // pictureBox1 + // + this.pictureBox1.Image = global::DiztinGUIsh.Properties.Resources.diz_4; + this.pictureBox1.Location = new System.Drawing.Point(7, 2); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(259, 259); + this.pictureBox1.TabIndex = 3; + this.pictureBox1.TabStop = false; + // + // ProgressDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(272, 316); + this.ControlBox = false; + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.progressBar1); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ProgressDialog"; + this.Padding = new System.Windows.Forms.Padding(9); + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Working..."; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ProgressBar progressBar1; + private System.Windows.Forms.PictureBox pictureBox1; + } +} diff --git a/DiztinGUIsh/window/dialog/ProgressDialog.cs b/DiztinGUIsh/window/dialog/ProgressDialog.cs new file mode 100644 index 00000000..a3e73e9b --- /dev/null +++ b/DiztinGUIsh/window/dialog/ProgressDialog.cs @@ -0,0 +1,44 @@ +using System.Windows.Forms; + +namespace DiztinGUIsh.window.dialog +{ + public partial class ProgressDialog : Form + { + private readonly string overrideText; + + public ProgressDialog(bool marquee = false, string textOverride = null) + { + InitializeComponent(); + progressBar1.Value = 0; + progressBar1.Maximum = 100; + + overrideText = textOverride; + UpdateProgressText(); + + if (marquee) + progressBar1.Style = ProgressBarStyle.Marquee; + } + + private void UpdateProgressText() + { + if (overrideText != null) + { + label2.Text = overrideText; + return; + } + + var percentDone = (int)(100 * ((float)progressBar1.Value / (float)progressBar1.Maximum)); + label2.Text = $@"{percentDone}%"; + } + + public void UpdateProgress(int i) + { + this.InvokeIfRequired(() => + { + progressBar1.Value = i; + UpdateProgressText(); + }); + } + } +} + diff --git a/DiztinGUIsh/window/dialog/ProgressDialog.resx b/DiztinGUIsh/window/dialog/ProgressDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DiztinGUIsh/window/dialog/ProgressDialog.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/DiztinGUIsh/window/usercontrols/BankLegend.Designer.cs b/DiztinGUIsh/window/usercontrols/BankLegend.Designer.cs new file mode 100644 index 00000000..d143b6a0 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/BankLegend.Designer.cs @@ -0,0 +1,60 @@ +namespace DiztinGUIsh.window.usercontrols +{ + partial class BankLegend + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.SuspendLayout(); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(472, 180); + this.flowLayoutPanel1.TabIndex = 0; + // + // BankLegend + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScroll = true; + this.AutoSize = true; + this.Controls.Add(this.flowLayoutPanel1); + this.Name = "BankLegend"; + this.Size = new System.Drawing.Size(472, 180); + this.Load += new System.EventHandler(this.BankLegend_Load); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/DiztinGUIsh/window/usercontrols/BankLegend.cs b/DiztinGUIsh/window/usercontrols/BankLegend.cs new file mode 100644 index 00000000..b505714c --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/BankLegend.cs @@ -0,0 +1,33 @@ +using System.Drawing; +using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; + +// shows all the legend items in a collection + +namespace DiztinGUIsh.window.usercontrols +{ + public partial class BankLegend : UserControl + { + public BankLegend() + { + InitializeComponent(); + } + + private void AddControl(string name, Color color) + { + flowLayoutPanel1.Controls.Add( + new BankLegendItem(name, color) + ); + } + + private void BankLegend_Load(object sender, System.EventArgs e) + { + var enums = Util.GetEnumColorDescriptions(); + foreach (var en in enums) + { + AddControl(en.Key.ToString(), Util.GetColorFromFlag(en.Key)); + } + } + } +} diff --git a/DiztinGUIsh/window/usercontrols/BankLegend.resx b/DiztinGUIsh/window/usercontrols/BankLegend.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/BankLegend.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/DiztinGUIsh/window/usercontrols/BankLegendItem.Designer.cs b/DiztinGUIsh/window/usercontrols/BankLegendItem.Designer.cs new file mode 100644 index 00000000..8d98ba09 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/BankLegendItem.Designer.cs @@ -0,0 +1,88 @@ +namespace DiztinGUIsh.window.usercontrols +{ + partial class BankLegendItem + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label1 = new System.Windows.Forms.Label(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.flowLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.BackColor = System.Drawing.SystemColors.ActiveCaption; + this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.pictureBox1.Location = new System.Drawing.Point(3, 3); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(29, 13); + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // label1 + // + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(38, 3); + this.label1.Margin = new System.Windows.Forms.Padding(3); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(141, 14); + this.label1.TabIndex = 1; + this.label1.Text = "Bank Legend Item Text"; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Controls.Add(this.pictureBox1); + this.flowLayoutPanel1.Controls.Add(this.label1); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(182, 17); + this.flowLayoutPanel1.TabIndex = 2; + // + // BankLegendItem + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.Controls.Add(this.flowLayoutPanel1); + this.Name = "BankLegendItem"; + this.Size = new System.Drawing.Size(182, 17); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.flowLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/DiztinGUIsh/window/usercontrols/BankLegendItem.cs b/DiztinGUIsh/window/usercontrols/BankLegendItem.cs new file mode 100644 index 00000000..7889356c --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/BankLegendItem.cs @@ -0,0 +1,18 @@ +using System.Drawing; +using System.Windows.Forms; + +// just shows one color and one label for Rom visualizer. like ("blue" = 8bit pointer) + +namespace DiztinGUIsh.window.usercontrols +{ + public partial class BankLegendItem : UserControl + { + public BankLegendItem(string labelText, Color color) + { + InitializeComponent(); + + label1.Text = labelText; + pictureBox1.BackColor = color; + } + } +} diff --git a/DiztinGUIsh/window/usercontrols/BankLegendItem.resx b/DiztinGUIsh/window/usercontrols/BankLegendItem.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/BankLegendItem.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/DiztinGUIsh/window/usercontrols/RomBankVisualizer.Designer.cs b/DiztinGUIsh/window/usercontrols/RomBankVisualizer.Designer.cs new file mode 100644 index 00000000..41d8a67b --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomBankVisualizer.Designer.cs @@ -0,0 +1,97 @@ +namespace DiztinGUIsh.window.usercontrols +{ + partial class RomBankVisualizer + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblBankName = new System.Windows.Forms.Label(); + this.romImage1 = new DiztinGUIsh.window.usercontrols.RomImage(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.flowLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // lblBankName + // + this.lblBankName.AutoSize = true; + this.lblBankName.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblBankName.Location = new System.Drawing.Point(3, 0); + this.lblBankName.MinimumSize = new System.Drawing.Size(60, 0); + this.lblBankName.Name = "lblBankName"; + this.lblBankName.Size = new System.Drawing.Size(60, 31); + this.lblBankName.TabIndex = 0; + this.lblBankName.Text = "C0"; + // + // romImage1 + // + this.romImage1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.romImage1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(128))))); + this.romImage1.Location = new System.Drawing.Point(66, 0); + this.romImage1.Margin = new System.Windows.Forms.Padding(0); + this.romImage1.MinimumSize = new System.Drawing.Size(400, 10); + this.romImage1.Name = "romImage1"; + this.romImage1.Project = null; + this.romImage1.Size = new System.Drawing.Size(400, 42); + this.romImage1.TabIndex = 1; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.flowLayoutPanel1.Controls.Add(this.lblBankName); + this.flowLayoutPanel1.Controls.Add(this.romImage1); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(466, 42); + this.flowLayoutPanel1.TabIndex = 2; + // + // RomBankVisualizer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.BackColor = System.Drawing.SystemColors.Control; + this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.Controls.Add(this.flowLayoutPanel1); + this.Name = "RomBankVisualizer"; + this.Size = new System.Drawing.Size(466, 42); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lblBankName; + private RomImage romImage1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/DiztinGUIsh/window/usercontrols/RomBankVisualizer.cs b/DiztinGUIsh/window/usercontrols/RomBankVisualizer.cs new file mode 100644 index 00000000..d060b176 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomBankVisualizer.cs @@ -0,0 +1,25 @@ +using System; +using System.Windows.Forms; +using Diz.Core.model; + +// this usercontrol shows: +// 1) a visual of one bank of ROM data +// 2) other GUI elements (like a label for the bank name) +// +// If you create a bunch of these you can view the entire ROM as a series of banks + +namespace DiztinGUIsh.window.usercontrols +{ + public partial class RomBankVisualizer : UserControl + { + public RomBankVisualizer(Project project, int startingRomOffset, int length, string bankName) + { + InitializeComponent(); + + romImage1.Project = project; + romImage1.RomVisual.RomStartingOffset = startingRomOffset; + romImage1.RomVisual.LengthOverride = length; + lblBankName.Text = bankName; + } + } +} diff --git a/DiztinGUIsh/window/usercontrols/RomBankVisualizer.resx b/DiztinGUIsh/window/usercontrols/RomBankVisualizer.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomBankVisualizer.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/DiztinGUIsh/window/usercontrols/RomFullVisualizer.Designer.cs b/DiztinGUIsh/window/usercontrols/RomFullVisualizer.Designer.cs new file mode 100644 index 00000000..3f882658 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomFullVisualizer.Designer.cs @@ -0,0 +1,64 @@ +namespace DiztinGUIsh.window.usercontrols +{ + partial class RomFullVisualizer + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.SuspendLayout(); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.flowLayoutPanel1.BackColor = System.Drawing.SystemColors.Control; + this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.MinimumSize = new System.Drawing.Size(100, 100); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(100, 100); + this.flowLayoutPanel1.TabIndex = 0; + // + // RomFullVisualizer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.Controls.Add(this.flowLayoutPanel1); + this.MinimumSize = new System.Drawing.Size(100, 100); + this.Name = "RomFullVisualizer"; + this.Size = new System.Drawing.Size(103, 103); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/DiztinGUIsh/window/usercontrols/RomFullVisualizer.cs b/DiztinGUIsh/window/usercontrols/RomFullVisualizer.cs new file mode 100644 index 00000000..b17375fc --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomFullVisualizer.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Windows.Forms; +using Diz.Core.model; + +// shows a collection of bank controls, so you can visualize the entire ROM + +namespace DiztinGUIsh.window.usercontrols +{ + public partial class RomFullVisualizer : UserControl + { + private Project project; + public Data Data => project?.Data; + + private ControlCollection FormControls => flowLayoutPanel1.Controls; + public Project Project + { + get => project; + set + { + DeleteControls(); + project = value; + if (project != null) + Init(); + } + } + + private void DeleteControls() + { + foreach (var rbv in BankControls.Where(rbv => FormControls.Contains(rbv))) + { + FormControls.Remove(rbv); + } + BankControls.Clear(); + } + + public RomFullVisualizer() + { + InitializeComponent(); + } + + public List BankControls = new List(); + + public void Init() + { + Debug.Assert(project != null); + + var bankSizeBytes = Data.GetBankSize(); + + for (var bank = 0; bank < Data.GetNumberOfBanks(); bank++) + { + var bankOffset = bank * bankSizeBytes; + var bankName = Data.GetBankName(bank); + + var bankControl = new RomBankVisualizer(project, bankOffset, bankSizeBytes, bankName); + + AddNewControl(bankControl); + } + } + + private void AddNewControl(RomBankVisualizer bankControl) + { + BankControls.Add(bankControl); + flowLayoutPanel1.Controls.Add(bankControl); + } + } +} diff --git a/DiztinGUIsh/window/usercontrols/RomFullVisualizer.resx b/DiztinGUIsh/window/usercontrols/RomFullVisualizer.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomFullVisualizer.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/DiztinGUIsh/window/usercontrols/RomImage.Designer.cs b/DiztinGUIsh/window/usercontrols/RomImage.Designer.cs new file mode 100644 index 00000000..b6726163 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomImage.Designer.cs @@ -0,0 +1,59 @@ +namespace DiztinGUIsh.window.usercontrols +{ + partial class RomImage + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.SuspendLayout(); + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Interval = 250; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // RomImage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(128))))); + this.DoubleBuffered = true; + this.Name = "RomImage"; + this.Size = new System.Drawing.Size(1142, 202); + this.Load += new System.EventHandler(this.RomImage_Load); + this.Paint += new System.Windows.Forms.PaintEventHandler(this.RomImage_Paint); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Timer timer1; + } +} diff --git a/DiztinGUIsh/window/usercontrols/RomImage.cs b/DiztinGUIsh/window/usercontrols/RomImage.cs new file mode 100644 index 00000000..9c5235e5 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomImage.cs @@ -0,0 +1,75 @@ +using System.Drawing; +using System.Windows.Forms; +using Diz.Core.model; +using Diz.Core.util; + +// this usercontrol is JUST to show the raw image data (no markup, formatting, etc) + +namespace DiztinGUIsh.window.usercontrols +{ + public partial class RomImage : UserControl + { + public RomVisual RomVisual { get; protected set; } = new RomVisual(); + public Project Project + { + get => RomVisual.Project; + set + { + RomVisual = new RomVisual + { + Project = value + }; + + // if there's a reason to track ROM byte changes, hook in here + // romVisual.MarkedDirty += RomVisual_MarkedDirty; + } + } + + public RomImage() + { + InitializeComponent(); + } + + private void RomImage_Load(object sender, System.EventArgs e) + { + UpdateDimensions(); + } + + private void RomImage_Paint(object sender, PaintEventArgs e) + { + Redraw(e.Graphics); + } + + private void Redraw(Graphics graphics = null) + { + if (RomVisual?.Bitmap == null) + return; + + graphics ??= CreateGraphics(); + + var width = RomVisual.Bitmap.Width; + var height = RomVisual.Bitmap.Height; + graphics.DrawImage(RomVisual.Bitmap, 0, 0, width, height); + } + + private void RedrawIfNeeded() + { + if (!Visible || !RomVisual.IsDirty) + return; + + RomVisual.Refresh(); + Redraw(); + } + + private void timer1_Tick(object sender, System.EventArgs e) + { + RedrawIfNeeded(); + } + + private void UpdateDimensions() + { + Width = RomVisual.Width; + Height = RomVisual.Height; + } + } +} diff --git a/DiztinGUIsh/window/usercontrols/RomImage.resx b/DiztinGUIsh/window/usercontrols/RomImage.resx new file mode 100644 index 00000000..1f666f26 --- /dev/null +++ b/DiztinGUIsh/window/usercontrols/RomImage.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 17, 17 + + \ No newline at end of file diff --git a/README.md b/README.md index ba976ce3..15796b11 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,20 @@ -# DiztinGUIsh +# DiztinGUIsh-FORK1 + +Note from fork author binary1230: +This is a fork of DiztinGUIsh https://github.com/Dotsarecool/DiztinGUIsh which adds some bells and whistles like: +- UX improvements (progress bars, easier GoTo search, open last project automatically, etc) +- Attach comments to labels themselves for documentation +- Integration and extension of functionality from @Gocha's fork: https://github.com/gocha/DiztinGUIsh for support for BSNESplus trace logs and usage maps. HIGHLY RECOMMEND if you're doing any serious disassembly work, it's super-useful. +- Various other small fixes/improvements + +Works fine with stock asar though, there's a bugfix you may want: +- https://github.com/binary1230/asar/tree/fix_relative_addressing/src/asar + +See discussion about the future of this fork here: +- https://github.com/Dotsarecool/DiztinGUIsh/issues/16 +- I hope to include all of the changes from this fork back upstream, but, maintaining this fork for the moment. + +# DiztinGUIsh original documentation follows A Super NES ROM Disassembler. @@ -45,4 +61,4 @@ Planned stuff: * Programmable data viewer to locate graphics easily * Setting a "base" per instruction for relocateable code * Option to put large data blocks into separate .bin files intead of in the .asm -* Scripting engine & API \ No newline at end of file +* Scripting engine & API