From 0cfed163a022330b43e7e1e1d2c4df19207bbb79 Mon Sep 17 00:00:00 2001 From: flobernd Date: Tue, 23 Aug 2016 16:11:42 +0200 Subject: [PATCH] Commited internally used InstructionEditor --- assets/InstructionEditor/.gitignore | 57 + .../Editors/formEditorCPUIDFeatureFlags.dfm | 318 ++ .../Editors/formEditorCPUIDFeatureFlags.pas | 191 + .../Editors/formEditorInstructionOperands.dfm | 199 + .../Editors/formEditorInstructionOperands.pas | 80 + .../Forms/Editors/formEditorX86Registers.dfm | 244 + .../Forms/Editors/formEditorX86Registers.pas | 263 + .../Forms/formCreateDefinition.dfm | 157 + .../Forms/formCreateDefinition.pas | 50 + .../InstructionEditor/Forms/formGenerator.dfm | 329 ++ .../InstructionEditor/Forms/formGenerator.pas | 395 ++ assets/InstructionEditor/Forms/formMain.dfm | 2508 +++++++++ assets/InstructionEditor/Forms/formMain.pas | 1822 +++++++ .../InstructionEditor/InstructionEditor.dpr | 29 + .../InstructionEditor/InstructionEditor.dproj | 593 +++ .../InstructionEditor/InstructionEditor.ico | Bin 0 -> 113473 bytes .../InstructionEditor/InstructionEditor.res | Bin 0 -> 115996 bytes .../InstructionEditor.skincfg | 55 + assets/InstructionEditor/README.md | 26 + assets/InstructionEditor/SynCrossPlatform.inc | 129 + .../SynCrossPlatformJSON.pas | 2150 ++++++++ assets/InstructionEditor/screenshot.png | Bin 0 -> 160907 bytes assets/InstructionEditor/untHelperClasses.pas | 278 + .../untInstructionEditor.pas | 4611 +++++++++++++++++ .../untInstructionFilters.pas | 607 +++ .../InstructionEditor/untPropertyEditors.pas | 384 ++ assets/InstructionEditor/untPropertyHints.pas | 75 + .../InstructionEditor/untTableGenerator.pas | 350 ++ 28 files changed, 15900 insertions(+) create mode 100644 assets/InstructionEditor/.gitignore create mode 100644 assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.dfm create mode 100644 assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.pas create mode 100644 assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.dfm create mode 100644 assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.pas create mode 100644 assets/InstructionEditor/Forms/Editors/formEditorX86Registers.dfm create mode 100644 assets/InstructionEditor/Forms/Editors/formEditorX86Registers.pas create mode 100644 assets/InstructionEditor/Forms/formCreateDefinition.dfm create mode 100644 assets/InstructionEditor/Forms/formCreateDefinition.pas create mode 100644 assets/InstructionEditor/Forms/formGenerator.dfm create mode 100644 assets/InstructionEditor/Forms/formGenerator.pas create mode 100644 assets/InstructionEditor/Forms/formMain.dfm create mode 100644 assets/InstructionEditor/Forms/formMain.pas create mode 100644 assets/InstructionEditor/InstructionEditor.dpr create mode 100644 assets/InstructionEditor/InstructionEditor.dproj create mode 100644 assets/InstructionEditor/InstructionEditor.ico create mode 100644 assets/InstructionEditor/InstructionEditor.res create mode 100644 assets/InstructionEditor/InstructionEditor.skincfg create mode 100644 assets/InstructionEditor/README.md create mode 100644 assets/InstructionEditor/SynCrossPlatform.inc create mode 100644 assets/InstructionEditor/SynCrossPlatformJSON.pas create mode 100644 assets/InstructionEditor/screenshot.png create mode 100644 assets/InstructionEditor/untHelperClasses.pas create mode 100644 assets/InstructionEditor/untInstructionEditor.pas create mode 100644 assets/InstructionEditor/untInstructionFilters.pas create mode 100644 assets/InstructionEditor/untPropertyEditors.pas create mode 100644 assets/InstructionEditor/untPropertyHints.pas create mode 100644 assets/InstructionEditor/untTableGenerator.pas diff --git a/assets/InstructionEditor/.gitignore b/assets/InstructionEditor/.gitignore new file mode 100644 index 0000000..8d5d458 --- /dev/null +++ b/assets/InstructionEditor/.gitignore @@ -0,0 +1,57 @@ +# Uncomment these types if you want even more clean repository. But be careful. +# It can make harm to an existing project source. Read explanations below. +# +# Resource files are binaries containing manifest, project icon and version info. +# They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. +#*.res +# +# Type library file (binary). In old Delphi versions it should be stored. +# Since Delphi 2009 it is produced from .ridl file and can safely be ignored. +#*.tlb +# +# Diagram Portfolio file. Used by the diagram editor up to Delphi 7. +# Uncomment this if you are not using diagrams or use newer Delphi version. +#*.ddp +# +# Visual LiveBindings file. Added in Delphi XE2. +# Uncomment this if you are not using LiveBindings Designer. +#*.vlb +# +# Deployment Manager configuration file for your project. Added in Delphi XE2. +# Uncomment this if it is not mobile development and you do not use remote debug feature. +#*.deployproj +# + +# Delphi compiler-generated binaries (safe to delete) +*.exe +*.dll +*.bpl +*.bpi +*.dcp +*.so +*.apk +*.drc +*.map +*.dres +*.rsm +*.tds +*.dcu +*.lib + +# Delphi autogenerated files (duplicated info) +*.cfg +*Resource.rc + +# Delphi local files (user-specific info) +*.local +*.identcache +*.projdata +*.tvsconfig +*.dsk + +# Delphi history and backups +__history/ +*.~* + +# Castalia statistics file +*.stat diff --git a/assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.dfm b/assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.dfm new file mode 100644 index 0000000..0e79900 --- /dev/null +++ b/assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.dfm @@ -0,0 +1,318 @@ +object frmEditorCPUIDFeatureFlags: TfrmEditorCPUIDFeatureFlags + Left = 0 + Top = 0 + BorderStyle = bsDialog + Caption = 'frmEditorCPUIDFeatureFlags' + ClientHeight = 321 + ClientWidth = 494 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object GroupBox: TGroupBox + AlignWithMargins = True + Left = 3 + Top = 279 + Width = 488 + Height = 39 + Margins.Top = 0 + Align = alBottom + TabOrder = 1 + DesignSize = ( + 488 + 39) + object btnCancel: TButton + Left = 368 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Cancel' + ImageIndex = 1 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 1 + OnClick = btnCancelClick + end + object btnApply: TButton + Left = 250 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Apply' + ImageIndex = 0 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 0 + OnClick = btnApplyClick + end + end + object VirtualTreeView: TVirtualStringTree + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 488 + Height = 273 + Align = alClient + Header.AutoSizeIndex = -1 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.MainColumn = -1 + Images = imgTreeView + TabOrder = 0 + TreeOptions.MiscOptions = [toCheckSupport, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick] + TreeOptions.PaintOptions = [toHideFocusRect, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowTreeLines, toShowVertGridLines, toThemeAware, toUseBlendedImages, toFullVertGridLines, toAlwaysHideSelection, toUseBlendedSelection] + TreeOptions.SelectionOptions = [toFullRowSelect] + OnChecked = VirtualTreeViewChecked + OnCollapsing = VirtualTreeViewCollapsing + OnFreeNode = VirtualTreeViewFreeNode + OnGetText = VirtualTreeViewGetText + OnGetImageIndex = VirtualTreeViewGetImageIndex + Columns = <> + end + object imgIcons16: TcxImageList + FormatVersion = 1 + DesignInfo = 2097208 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 20000000000000040000000000000000000000000000000000000000001B0000 + 0033000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000001B00000000004B29A00089 + 49FF008747FF008747FF008747FF008747FF008747FF008747FF008747FF0087 + 47FF008747FF008747FF008747FF008949FF004B29A000000000008949FF07E4 + A4FF07E5A5FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4 + A4FF07E4A4FF07E4A4FF07E5A5FF07E4A4FF008949FF00000000008747FF13E7 + AAFF00DB9BFF00DA9AFF00DA99FF00DA99FF00DA99FF00DA99FF00DA9AFF00DA + 9AFF00DA9AFF00DA9AFF00DB9BFF13E7AAFF008747FF00000000008746FF1EE7 + AEFF00D699FF00D698FF00D495FF00D391FF00D391FF00D495FF00D698FF00D6 + 99FF00D699FF00D699FF00D699FF1EE7AEFF008746FF00000000008746FF2AE9 + B3FF00D299FF00D095FF00CC8DFFFFFFFFFFFFFFFFFF00CC8DFF00D096FF00D2 + 99FF00D29AFF00D29AFF00D29AFF2AE9B3FF008746FF00000000008746FF35EA + B7FF00CD96FF00C98DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C98DFF00CD + 96FF00CF99FF00CF9AFF00CF9AFF36EAB8FF008746FF00000000008745FF3EEB + BBFF00C692FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C4 + 8DFF00C995FF00CA99FF00CA99FF41ECBDFF008745FF00000000008745FF4BED + BFFF00C393FFFFFFFFFFFFFFFFFF00C08BFF00BF8BFFFFFFFFFFFFFFFFFFFFFF + FFFF00C18EFF00C596FF00C699FF4DEDC0FF008745FF00000000008744FF58EF + C5FF00C197FF00BF93FF00C093FF00C196FF00C196FF00BC8EFFFFFFFFFFFFFF + FFFFFFFFFFFF00BC8EFF00C096FF58EFC5FF008744FF00000000008744FF64F1 + CAFF00BE99FF00C099FF00C09AFF00C19BFF00C19AFF00BE97FF00B98EFFFFFF + FFFFFFFFFFFFFFFFFFFF00BA92FF62F0C9FF008744FF00000000008744FF71F2 + CFFF00BB99FF00BD9AFF00BE9BFF00BE9BFF00BE9BFF00BE9AFF00BB96FF00B6 + 8EFFFFFFFFFFFFFFFFFF00B792FF6EF2CDFF008744FF00000000008744FF7BF4 + D3FF00B699FF00B89AFF00B99BFF00B99BFF00B99BFF00B99BFF00B89AFF00B7 + 98FF00B494FF00B493FF00B496FF7AF3D3FF008744FF00000000008744FF86F5 + D8FF00B198FF00B299FF00B399FF00B399FF00B399FF00B399FF00B399FF00B2 + 99FF00B298FF00B297FF00B197FF86F5D8FF008744FF00000000008946FF8AF2 + D9FF92F6DEFF93F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6 + DDFF94F6DDFF93F6DDFF92F6DEFF8AF2D9FF008946FF00000000004B29880089 + 46FF008743FF008643FF008643FF008643FF008643FF008643FF008643FF0086 + 43FF008643FF008643FF008743FF008946FF004B298800000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0013000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000003300000013000000001117 + 487E2D3CC0FF2B3BBEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3A + BEFF2A3ABEFF2A3ABEFF2A3ABEFF2B3BBEFF2D3CC0FF1117487E000000002D3D + C0FF6172FAFF6A7DFFFF697CFFFF6D80FFFF7485FFFF7788FFFF7788FFFF7788 + FFFF7485FFFF6D7FFFFF697CFFFF6A7DFFFF6172FAFF2D3DC0FF000000002B3B + BEFF6578FFFF5F73FEFF6F80FFFF7989FFFF5868E6FF3E4ED0FF3F4FD1FF3E4E + D0FF5767E5FF7887FEFF6E7FFFFF5F73FEFF6578FFFF2B3BBEFF000000002B3B + BEFF5D72FFFF697BFEFF7785F8FF3949CAFF1E2A89B70B0F303F07091D26070A + 1F552636B9FF5A6FFDFF576CFAFF687AFCFF5D71FFFF2B3BBEFF000000002C3B + BFFF586EFEFF8593FFFF3B4BCAFF0E143F530000000000000000000000332B3A + BDFF5970FEFF546AFBFF7F8FFFFF8290FDFF586EFEFF2C3BBFFF000000002C3C + BFFF536BFCFF707CE7FF1E2A89BB0000000000000000000000332C3BBEFF546B + FBFF5067F8FF8796FFFF3B4BCDFF6D79E5FF536AFCFF2C3CBFFF000000002D3D + BFFF4F68FBFF4E5BD1FF0B0F304500000000000000332C3BBEFF4E66F9FF4A63 + F6FF8C99FFFF2737BCFF07091F314D5AD0FF4F68FBFF2D3DBFFF000000002E3D + C0FF4A64F9FF3446CDFF07091D2D000000332D3CBEFF4862F7FF445DF4FF8E9C + FFFF2838BDFF0000000007091D313446CDFF4A64F9FF2E3DC0FF000000002E3D + C0FF4660F7FF3445CCFF080A20552D3CBEFF445DF4FF3F59F1FF929FFFFF2838 + BDFF00000000000000000C0F305B3445CDFF4660F7FF2E3DC0FF000000002E3E + C0FF405BF3FF384DDBFF2E3CBCFF3E59F2FF3954EFFF95A2FFFF2838BDFF0000 + 00000000000000000011222D8AC5384EDCFF405BF3FF2E3EC0FF000000002F3E + C0FF3955F0FF3951EAFF3954EEFF3651EDFF9AA5FFFF2838BDFF000000080000 + 000D0000002510153F753041C8FF3A53EBFF3A55F0FF2F3EC0FF000000002E3D + BFFF3A54EFFF324DE9FF334DE9FF324DEBFF2A39BCFF070A1F5507091D510B0F + 3065212D8AC52F41C8FF314CE4FF324DEBFF3A54EFFF2E3DBFFF000000002B3B + BFFF6A7DFDFF334EE8FF2643E4FF2642E3FF253CD4FF2739C7FF273AC8FF273A + C8FF263DD5FF2743E5FF2643E6FF334EE9FF6A7DFDFF2B3BBFFF000000002B3B + C0FF929DFCFFAAB3FFFFAAB3FFFFAAB3FFFFAAB4FFFFAAB4FFFFAAB4FFFFAAB4 + FFFFAAB4FFFFAAB3FFFFAAB3FFFFAAB3FFFF929DFCFF2B3BC0FF000000001117 + 485E2A3AC0FF2838BFFF2737BEFF2737BEFF2737BEFF2737BFFF2737BFFF2737 + BFFF2737BEFF2737BEFF2737BEFF2838BFFF2A3AC0FF1117485E} + end> + end + object imgTreeView: TcxImageList + FormatVersion = 1 + DesignInfo = 2097288 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000300000 + 0033000000330000003300000033000000330000003300000033000000330000 + 00330000003300000033000000330000002F00000000000000004190C6F24398 + D2FF4094D0FF3E92CFFF3E92CEFF3F92CEFF3F92CEFF3F92CEFF3F92CEFF3F92 + CEFF3F92CEFF3F92CEFF3F93CFFF3D8BC2F00000000E000000004499D2FF3F94 + D0FFABFBFFFF9BF3FFFF92F1FFFF93F1FFFF93F1FFFF93F1FFFF93F1FFFF93F1 + FFFF93F1FFFF93F1FFFFA6F8FFFF65B8E3FF122A3A5F000000004398D2FF4FA6 + D9FF8EDAF5FFA2EEFFFF82E5FEFF84E5FEFF84E5FEFF85E6FEFF85E6FEFF85E6 + FEFF85E6FEFF84E6FEFF96EBFFFF8CD8F5FF296088B8000000004296D1FF6BBE + E8FF6DBDE6FFBBF2FFFF75DEFDFF77DEFCFF78DEFCFF7BDFFCFF7DDFFCFF7DDF + FCFF7DDFFCFF7CDFFCFF80E0FDFFADF0FFFF4D9DD3FF0000000E4095D0FF8AD7 + F5FF44A1D8FFDDFDFFFFDAFAFFFFDBFAFFFFDEFAFFFF74DCFCFF76DBFAFF75DA + FAFF74DAFAFF74DAFAFF72D9FAFFA1E8FFFF7CBFE6FF12293A5E3E94D0FFABF0 + FFFF449DD6FF368CCBFF368CCBFF368CCBFF378BCBFF5CBEEAFF6FD9FBFF6AD6 + FAFF68D5F9FF67D4F9FF66D4F9FF82DEFCFFAAE0F6FF296088B93D92CFFFB9F4 + FFFF73DBFBFF6BCCF2FF6CCDF3FF6CCEF3FF6DCEF3FF479CD4FF56BAE9FFDAF8 + FFFFD7F6FFFFD6F6FFFFD5F6FFFFD5F7FFFFDBFCFFFF3E94D0FF3C92CFFFC0F3 + FFFF71DAFBFF74DBFBFF75DBFCFF75DBFCFF76DCFCFF73DAFAFF449CD4FF378C + CBFF368CCBFF358CCCFF348DCCFF3890CEFF3D94D0FF3E8CC2EB3B92CFFFCAF6 + FFFF69D5F9FF6CD5F9FF6BD5F9FF69D5F9FF69D5FAFF6AD7FBFF68D4FAFF5EC7 + F1FF5EC7F2FF5DC8F2FFB4E3F8FF3D94D0FF1A3B5269000000003B92CFFFD5F7 + FFFF60D1F9FF61D0F8FFB4EBFDFFD9F6FFFFDAF8FFFFDAF8FFFFDBF9FFFFDCFA + FFFFDCFAFFFFDCFBFFFFE0FFFFFF3E95D0FF0E1F2A33000000003D94D0FFDCFC + FFFFD8F7FFFFD8F7FFFFDBFAFFFF358ECDFF3991CEFF3A92CFFF3A92CFFF3A92 + CFFF3A92CFFF3B92CFFF3D94D0FF3880B1D700000000000000002E6991B03D94 + D0FF3A92CFFF3A92CFFF3D94D0FF367CACD20000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000002200000033000000330000 + 0033000000330000003300000033000000330000002300000000000000000000 + 0030000000330000003300000033000000337B5426B9B78241FFB68142FFB681 + 42FFB68141FFB68142FFB68142FFB78343FF805C2FC00000000000000000A8A8 + A6F2AFAFADFFAEAEABFFAEAFADFFAEB3B8FFB67C35FFF8CB85FFF4C47AFFF3C3 + 77FFF3C174FFF3C377FFF4C47AFFF9CC87FFB78243FF0000000000000000B0B0 + ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB27730FFF2C787FFEAB76CFFE9B3 + 65FFFFFFFFFFE9B365FFEAB76CFFF2C98AFFB68141FF0000000000000000AEAE + ABFFFFFFFFFFFDFDFCFFFDFEFEFFFFFFFFFFB0752EFFEEC78EFFE2AE61FFE0A9 + 58FFFFFFFFFFE0A958FFE2AE61FFEEC990FFB68141FF0000000000000000ADAD + ABFFFFFFFFFFF8F8F8FFF9FAFBFFFEFFFFFFB0752DFFEBC994FFDAA252FFD89D + 47FFFFFFFFFFD89D47FFDAA252FFECCA97FFB68140FF0000000000000000ADAD + ABFFFFFFFFFFF6F5F5FFF7F7F8FFFCFFFFFFB0752CFFEACA9CFFF3E4CEFFFFFF + FFFFFFFFFFFFFFFFFFFFF3E4CEFFEBCC9FFFB7813FFF0000000000000000ADAD + ABFFFFFFFFFFF3F2F2FFF4F3F4FFF8FBFFFFB0742AFFE8CA9EFFE6C79AFFE4C4 + 95FFE4C494FFE5C595FFE6C89BFFEACEA4FFB88241FF0000000000000000ADAD + ABFFFFFFFFFFF0F0EFFFF0F1F0FFF3F6F9FFCAA578FFB1742BFFB1752CFFB175 + 2BFFB1742BFFB2762DFFB57A33FFB8803CFF8F6634C40000000000000000ADAD + ABFFFFFFFFFFEEECEBFFEEEDECFFEFEFEFFFF1F3F5FFF1F5F9FFF2F5FBFFF2F6 + FBFFF2F6FBFFFFFFFFFFAEB6BDFF00000000000000000000000000000000ADAD + ABFFFFFFFFFFEAE9E9FFEBEAEAFFEBEAEAFFEAE9EAFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFAEB0B1FF00000000000000000000000000000000ADAD + ABFFFFFFFFFFE7E7E6FFE8E8E7FFE8E8E7FFE6E6E5FFFFFFFFFFCBCBCAFFA6A6 + A4FFA5A5A2FFFFFFFFFFAFAFADFF00000000000000000000000000000000AEAE + ABFFFFFFFFFFE4E3E2FFE5E4E3FFE5E4E3FFE4E3E2FFFFFFFFFFA6A6A4FFFBFB + FAFFFFFFFFFFE9E9E9FF777775AC00000000000000000000000000000000AEAE + ACFFFFFFFFFFE0DFDEFFE1DFDEFFE1DFDEFFE0DFDEFFFFFFFFFFA5A5A2FFFFFF + FFFFE7E7E8FF737371A70000000000000000000000000000000000000000AFAF + ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9E9 + E9FF717170A6000000000000000000000000000000000000000000000000A8A8 + A6EFB0B0ADFFAEAEACFFAEAEABFFAEAEABFFADAEABFFAEAEABFFAFAFADFFA2A2 + A0EA000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0023000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000230000000000000000000000006042 + 1FC08A5E2BFF895E2BFF895D2AFF895D2AFF895D29FF895C29FF895D29FF895D + 2AFF895D2AFF895E2BFF8A5E2BFF60421FC00000000000000000000000008A5E + 2BFFAE8D5BFFAA8653FFA98551FFA7824DFFA6814BFFA57F49FFA6814BFFA782 + 4DFFA98551FFAA8653FFAE8D5BFF8A5E2BFF000000000000000000000000895D + 2AFFB18E5CFFA27E4AFF9E7841FFD8C8B1FF966D32FFFFFFFFFF966D32FFD8C8 + B1FF9E7841FFA27E4AFFB18E5CFF895D2AFF000000000000000000000000895D + 2AFFB3925EFF9E7742FFD7C6B0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFD7C6B0FF9E7742FFB3925EFF895D2AFF000000000000000000000000885C + 29FFB69562FF9A723DFF91652AFFFFFFFFFFCFBBA0FF916429FFCFBBA0FFFFFF + FFFF91652AFF9A723DFFB69562FF885C29FF000000000000000000000000885C + 29FFBA9865FF956D37FFFFFFFFFFFFFFFFFF8D6125FF936931FF8D6125FFFFFF + FFFFFFFFFFFF956D37FFBA9865FF885C29FF000000000000000000000000885C + 29FFBC9C6AFF946B36FF8B5E23FFFFFFFFFFCBB89DFF8A5D21FFCBB89DFFFFFF + FFFF8B5E23FF946B36FFBC9C6AFF885C29FF000000000000000000000000885C + 29FFC1A06EFF936934FFD2C0AAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFD1BFAAFF926833FFC1A06EFF885C29FF000000000000000000000000885C + 29FFC4A373FF916834FF8E632DFFD2C0A9FF86581DFFFFFFFFFF84551AFFCEBC + A5FF8B5F29FF8E6530FFC3A271FF875C28FF000000000000000000000000875C + 28FFC8A777FF906633FF916833FF906631FF8F642FFF8C602AFF9F7843FFC3A0 + 6DFFC4A270FFC4A271FFC6A474FF875B28FF000000000000000000000000875B + 28FFCAAB7BFF8E6330FF916734FF926734FF916633FF8D622FFFC9A978FF9A72 + 3EFF7E4F18FF7D4E18FFC8A876FF875B28FF000000000000000000000000875B + 28FFCEAF7EFF8A5F2CFF8D6330FF8E6431FF8D6330FF8A5F2CFFCCAD7BFF7E4F + 18FFF6FFFFFFFFFFFFFFAF8956FF5C3E1CAC000000000000000000000000875B + 28FFD1B181FF845824FF875B27FF885C28FF875B27FF845724FFCEAD7DFF7C4D + 16FFFFFFFFFFAE8651FF573A17A700000000000000000000000000000000885C + 29FFD5B687FFD4B585FFD4B586FFD5B586FFD4B586FFD3B485FFD1B282FFD1B2 + 81FFB38E59FF573917A600000000000000000000000000000000000000008159 + 29EF885C29FF875B28FF875B28FF875B28FF875B28FF865B27FF865A27FF875B + 27FF7D5426EA0000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000230000 + 0033000000330000003300000033000000330000003300000033000000330000 + 00330000003300000033000000230000000000000000000000007B7B7AC0AFAF + ADFFAEAEACFFAEAEACFFAEAFADFFAFB0AEFFAFB0AFFFAFB0AEFFAFAFAEFFAFAF + ADFFAFAFADFFB0B0AEFF7B7B79BF000000000000000000000000B0B0ADFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFB1B1AEFF000000330000003300000023AEAEABFFFFFF + FFFFFFFFFFFFF7F3EFFFC38450FFA54800FFA64900FFA74900FFAA6B37FFACA8 + A2FFABAFB0FFABACAAFFADADABFFAFAFADFFB0B1AEFF7B7B7AC0AEAEACFFFFFF + FFFFF5EFECFFAB5308FFC8843EFFE4B270FFE3AF6DFFE5B271FFCB8741FFAF57 + 0DFFEFE9E6FFF5F7FAFFF4F5F6FFF6F7F8FFFFFFFFFFB0B1AEFFAEAFADFFFFFF + FFFFC1844EFFC67F38FFDEA865FFDAA15BFFFFFFFFFFDAA25BFFDEA966FFC881 + 3AFFBE814DFFFFFFFFFFFFFFFFFFB48C75FFFFFFFFFFAFB0AEFFAFAFAEFFFFFF + FFFFA64900FFD89F5AFFD69C57FFD3944CFFFFFFFFFFD3944CFFD69C57FFD9A0 + 5BFFA94E00FFB89687FFB7907CFFB68E79FFFFFFFFFFAFAFADFFAFB0AFFFFFFF + FFFFA74A00FFDCAB71FFD19149FFCF8C41FFFFFFFFFFCF8C41FFD19149FFDDAB + 72FFA94D00FFDEE8EFFFDCE0E2FFDCDFDFFFFFFFFFFFAFAFADFFAFAFAEFFFFFF + FFFFA64800FFE8CAA5FFCB8639FFCA8436FFC88130FFCA8436FFCB8639FFE9CB + A6FFA84B00FFFFFFFFFFFFFFFFFFB58D76FFFFFFFFFFAFAFADFFAEAFADFFFFFF + FFFFBD7E48FFCF9763FFE0B789FFC88236FFFFFFFFFFC88236FFE0B78AFFD099 + 65FFB47641FFB79381FFB68E7AFFB68D77FFFFFFFFFFAFB0AEFFAEAEACFFFFFF + FFFFE5DFDBFFAA5206FFCC945EFFE9CBAAFFE8C9A7FFE9CBABFFCE9660FFAF56 + 0BFFF8F4F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0B0AEFFAEAEABFFFFFF + FFFFE9ECEEFFE4DEDAFFBD7E47FFA74800FFA74900FFA84900FFAC6D37FFAEA9 + A2FFACAFB0FFACADACFFAEAFADFFAFAFADFFB0B0AEFF7C7C7AB0AEAEABFFFFFF + FFFFE3E5E4FFE6E9EAFFE8EDF1FFE9F0F5FFE8F0F5FFFFFFFFFFAAB0B2FFEDF0 + F1FFFFFFFFFFEBEBECFF727271A4000000000000000000000000AEAEACFFFFFF + FFFFE0DFDEFFE1E0DFFFE1E1E1FFE1E2E1FFE1E1E1FFFFFFFFFFA6A7A5FFFFFF + FFFFE8E8E8FF737371A700000000000000000000000000000000AFAFADFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9E9 + E9FF717170A60000000000000000000000000000000000000000A8A8A6EFB0B0 + ADFFAEAEACFFAEAEABFFAEAEABFFAEAEABFFADAEABFFAEAEABFFAFAFADFFA2A2 + A0EA000000000000000000000000000000000000000000000000} + end> + end +end diff --git a/assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.pas b/assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.pas new file mode 100644 index 0000000..ff02155 --- /dev/null +++ b/assets/InstructionEditor/Forms/Editors/formEditorCPUIDFeatureFlags.pas @@ -0,0 +1,191 @@ +unit formEditorCPUIDFeatureFlags; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, untInstructionEditor, cxOI, Vcl.StdCtrls, System.ImageList, + Vcl.ImgList, cxGraphics, VirtualTrees; + +type + TfrmEditorCPUIDFeatureFlags = class(TForm) + GroupBox: TGroupBox; + btnCancel: TButton; + imgIcons16: TcxImageList; + btnApply: TButton; + VirtualTreeView: TVirtualStringTree; + imgTreeView: TcxImageList; + procedure btnApplyClick(Sender: TObject); + procedure btnCancelClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure VirtualTreeViewChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure VirtualTreeViewCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; + var Allowed: Boolean); + procedure VirtualTreeViewFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure VirtualTreeViewGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); + procedure VirtualTreeViewGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + strict private + FApplyChanges: Boolean; + FFeatureFlags: TCPUIDFeatureFlagSet; + strict private + procedure SetFeatureFlags(const Value: TCPUIDFeatureFlagSet); + public + property ApplyChanges: Boolean read FApplyChanges; + property FeatureFlags: TCPUIDFeatureFlagSet read FFeatureFlags write SetFeatureFlags; + end; + +var + frmEditorCPUIDFeatureFlags: TfrmEditorCPUIDFeatureFlags; + +implementation + +uses + System.TypInfo; + +{$R *.dfm} + +type + PNodeData = ^TNodeData; + TNodeData = record + public + Text: String; + Value: TCPUIDFeatureFlag; + end; + +procedure TfrmEditorCPUIDFeatureFlags.btnApplyClick(Sender: TObject); +begin + FApplyChanges := true; + Close; +end; + +procedure TfrmEditorCPUIDFeatureFlags.btnCancelClick(Sender: TObject); +begin + Close; +end; + +procedure TfrmEditorCPUIDFeatureFlags.FormCreate(Sender: TObject); +var + Root, + Node: PVirtualNode; + NodeData: PNodeData; + C: TCPUIDFeatureFlag; + S: String; +begin + FFeatureFlags := []; + VirtualTreeView.NodeDataSize := SizeOf(TNodeData); + VirtualTreeView.BeginUpdate; + try + Root := VirtualTreeView.AddChild(nil); + NodeData := VirtualTreeView.GetNodeData(Root); + NodeData^.Text := 'CPUID Feature Flags'; + for C := Low(TCPUIDFeatureFlag) to High(TCPUIDFeatureFlag) do + begin + Node := VirtualTreeView.AddChild(Root); + VirtualTreeView.CheckType[Node] := ctCheckBox; + NodeData := VirtualTreeView.GetNodeData(Node); + S := GetEnumName(TypeInfo(TCPUIDFeatureFlag), Ord(C)); + Delete(S, 1, 2); + NodeData^.Text := S; + NodeData^.Value := C; + end; + VirtualTreeView.Expanded[Root] := true; + finally + VirtualTreeView.EndUpdate; + end; +end; + +procedure TfrmEditorCPUIDFeatureFlags.SetFeatureFlags(const Value: TCPUIDFeatureFlagSet); +var + Node: PVirtualNode; + NodeData: PNodeData; +begin + VirtualTreeView.BeginUpdate; + try + Node := VirtualTreeView.GetFirst; + while (Assigned(Node)) do + begin + NodeData := VirtualTreeView.GetNodeData(Node); + if (Assigned(NodeData) and (Node.Parent <> VirtualTreeView.RootNode)) then + begin + if (NodeData^.Value in Value) then + begin + VirtualTreeView.CheckState[Node] := csCheckedNormal; + end else + begin + VirtualTreeView.CheckState[Node] := csUncheckedNormal; + end; + end; + Node := VirtualTreeView.GetNext(Node); + end; + finally + VirtualTreeView.EndUpdate; + end; +end; + +procedure TfrmEditorCPUIDFeatureFlags.VirtualTreeViewChecked(Sender: TBaseVirtualTree; + Node: PVirtualNode); +var + NodeData: PNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + case Sender.CheckState[Node] of + csCheckedNormal: + FFeatureFlags := FFeatureFlags + [NodeData^.Value]; + csUncheckedNormal: + FFeatureFlags := FFeatureFlags - [NodeData^.Value]; + end; + end; +end; + +procedure TfrmEditorCPUIDFeatureFlags.VirtualTreeViewCollapsing(Sender: TBaseVirtualTree; + Node: PVirtualNode; var Allowed: Boolean); +begin + Allowed := false; +end; + +procedure TfrmEditorCPUIDFeatureFlags.VirtualTreeViewFreeNode(Sender: TBaseVirtualTree; + Node: PVirtualNode); +var + NodeData: PNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + Finalize(NodeData^); + end; +end; + +procedure TfrmEditorCPUIDFeatureFlags.VirtualTreeViewGetImageIndex(Sender: TBaseVirtualTree; + Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; + var ImageIndex: Integer); +begin + ImageIndex := -1; + if (Kind in [ikNormal, ikSelected]) then + begin + if (Node.Parent = Sender.RootNode) then + begin + ImageIndex := 0; + end else + begin + ImageIndex := 1; + end; + end; +end; + +procedure TfrmEditorCPUIDFeatureFlags.VirtualTreeViewGetText(Sender: TBaseVirtualTree; + Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + NodeData: PNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + CellText := NodeData^.Text; + end; +end; + +end. diff --git a/assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.dfm b/assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.dfm new file mode 100644 index 0000000..9c8ac05 --- /dev/null +++ b/assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.dfm @@ -0,0 +1,199 @@ +object frmEditorInstructionOperands: TfrmEditorInstructionOperands + Left = 0 + Top = 0 + BorderStyle = bsDialog + Caption = 'frmEditorInstructionOperands' + ClientHeight = 462 + ClientWidth = 640 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + OnDestroy = FormDestroy + PixelsPerInch = 96 + TextHeight = 13 + object GroupBox: TGroupBox + AlignWithMargins = True + Left = 3 + Top = 420 + Width = 634 + Height = 39 + Margins.Top = 0 + Align = alBottom + TabOrder = 0 + DesignSize = ( + 634 + 39) + object btnCancel: TButton + Left = 514 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Cancel' + ImageIndex = 1 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 1 + OnClick = btnCancelClick + end + object btnApply: TButton + Left = 396 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Apply' + ImageIndex = 0 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 0 + OnClick = btnApplyClick + end + end + object PageControl1: TPageControl + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 634 + Height = 414 + ActivePage = TabSheet1 + Align = alClient + TabOrder = 1 + object TabSheet1: TTabSheet + Caption = 'Instruction Operands' + object GroupBox1: TGroupBox + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 238 + Height = 380 + Align = alLeft + TabOrder = 0 + end + end + end + object imgIcons16: TcxImageList + FormatVersion = 1 + DesignInfo = 1049152 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 20000000000000040000000000000000000000000000000000000000001B0000 + 0033000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000001B00000000004B29A00089 + 49FF008747FF008747FF008747FF008747FF008747FF008747FF008747FF0087 + 47FF008747FF008747FF008747FF008949FF004B29A000000000008949FF07E4 + A4FF07E5A5FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4 + A4FF07E4A4FF07E4A4FF07E5A5FF07E4A4FF008949FF00000000008747FF13E7 + AAFF00DB9BFF00DA9AFF00DA99FF00DA99FF00DA99FF00DA99FF00DA9AFF00DA + 9AFF00DA9AFF00DA9AFF00DB9BFF13E7AAFF008747FF00000000008746FF1EE7 + AEFF00D699FF00D698FF00D495FF00D391FF00D391FF00D495FF00D698FF00D6 + 99FF00D699FF00D699FF00D699FF1EE7AEFF008746FF00000000008746FF2AE9 + B3FF00D299FF00D095FF00CC8DFFFFFFFFFFFFFFFFFF00CC8DFF00D096FF00D2 + 99FF00D29AFF00D29AFF00D29AFF2AE9B3FF008746FF00000000008746FF35EA + B7FF00CD96FF00C98DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C98DFF00CD + 96FF00CF99FF00CF9AFF00CF9AFF36EAB8FF008746FF00000000008745FF3EEB + BBFF00C692FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C4 + 8DFF00C995FF00CA99FF00CA99FF41ECBDFF008745FF00000000008745FF4BED + BFFF00C393FFFFFFFFFFFFFFFFFF00C08BFF00BF8BFFFFFFFFFFFFFFFFFFFFFF + FFFF00C18EFF00C596FF00C699FF4DEDC0FF008745FF00000000008744FF58EF + C5FF00C197FF00BF93FF00C093FF00C196FF00C196FF00BC8EFFFFFFFFFFFFFF + FFFFFFFFFFFF00BC8EFF00C096FF58EFC5FF008744FF00000000008744FF64F1 + CAFF00BE99FF00C099FF00C09AFF00C19BFF00C19AFF00BE97FF00B98EFFFFFF + FFFFFFFFFFFFFFFFFFFF00BA92FF62F0C9FF008744FF00000000008744FF71F2 + CFFF00BB99FF00BD9AFF00BE9BFF00BE9BFF00BE9BFF00BE9AFF00BB96FF00B6 + 8EFFFFFFFFFFFFFFFFFF00B792FF6EF2CDFF008744FF00000000008744FF7BF4 + D3FF00B699FF00B89AFF00B99BFF00B99BFF00B99BFF00B99BFF00B89AFF00B7 + 98FF00B494FF00B493FF00B496FF7AF3D3FF008744FF00000000008744FF86F5 + D8FF00B198FF00B299FF00B399FF00B399FF00B399FF00B399FF00B399FF00B2 + 99FF00B298FF00B297FF00B197FF86F5D8FF008744FF00000000008946FF8AF2 + D9FF92F6DEFF93F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6 + DDFF94F6DDFF93F6DDFF92F6DEFF8AF2D9FF008946FF00000000004B29880089 + 46FF008743FF008643FF008643FF008643FF008643FF008643FF008643FF0086 + 43FF008643FF008643FF008743FF008946FF004B298800000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0013000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000003300000013000000001117 + 487E2D3CC0FF2B3BBEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3A + BEFF2A3ABEFF2A3ABEFF2A3ABEFF2B3BBEFF2D3CC0FF1117487E000000002D3D + C0FF6172FAFF6A7DFFFF697CFFFF6D80FFFF7485FFFF7788FFFF7788FFFF7788 + FFFF7485FFFF6D7FFFFF697CFFFF6A7DFFFF6172FAFF2D3DC0FF000000002B3B + BEFF6578FFFF5F73FEFF6F80FFFF7989FFFF5868E6FF3E4ED0FF3F4FD1FF3E4E + D0FF5767E5FF7887FEFF6E7FFFFF5F73FEFF6578FFFF2B3BBEFF000000002B3B + BEFF5D72FFFF697BFEFF7785F8FF3949CAFF1E2A89B70B0F303F07091D26070A + 1F552636B9FF5A6FFDFF576CFAFF687AFCFF5D71FFFF2B3BBEFF000000002C3B + BFFF586EFEFF8593FFFF3B4BCAFF0E143F530000000000000000000000332B3A + BDFF5970FEFF546AFBFF7F8FFFFF8290FDFF586EFEFF2C3BBFFF000000002C3C + BFFF536BFCFF707CE7FF1E2A89BB0000000000000000000000332C3BBEFF546B + FBFF5067F8FF8796FFFF3B4BCDFF6D79E5FF536AFCFF2C3CBFFF000000002D3D + BFFF4F68FBFF4E5BD1FF0B0F304500000000000000332C3BBEFF4E66F9FF4A63 + F6FF8C99FFFF2737BCFF07091F314D5AD0FF4F68FBFF2D3DBFFF000000002E3D + C0FF4A64F9FF3446CDFF07091D2D000000332D3CBEFF4862F7FF445DF4FF8E9C + FFFF2838BDFF0000000007091D313446CDFF4A64F9FF2E3DC0FF000000002E3D + C0FF4660F7FF3445CCFF080A20552D3CBEFF445DF4FF3F59F1FF929FFFFF2838 + BDFF00000000000000000C0F305B3445CDFF4660F7FF2E3DC0FF000000002E3E + C0FF405BF3FF384DDBFF2E3CBCFF3E59F2FF3954EFFF95A2FFFF2838BDFF0000 + 00000000000000000011222D8AC5384EDCFF405BF3FF2E3EC0FF000000002F3E + C0FF3955F0FF3951EAFF3954EEFF3651EDFF9AA5FFFF2838BDFF000000080000 + 000D0000002510153F753041C8FF3A53EBFF3A55F0FF2F3EC0FF000000002E3D + BFFF3A54EFFF324DE9FF334DE9FF324DEBFF2A39BCFF070A1F5507091D510B0F + 3065212D8AC52F41C8FF314CE4FF324DEBFF3A54EFFF2E3DBFFF000000002B3B + BFFF6A7DFDFF334EE8FF2643E4FF2642E3FF253CD4FF2739C7FF273AC8FF273A + C8FF263DD5FF2743E5FF2643E6FF334EE9FF6A7DFDFF2B3BBFFF000000002B3B + C0FF929DFCFFAAB3FFFFAAB3FFFFAAB3FFFFAAB4FFFFAAB4FFFFAAB4FFFFAAB4 + FFFFAAB4FFFFAAB3FFFFAAB3FFFFAAB3FFFF929DFCFF2B3BC0FF000000001117 + 485E2A3AC0FF2838BFFF2737BEFF2737BEFF2737BEFF2737BFFF2737BFFF2737 + BFFF2737BEFF2737BEFF2737BEFF2838BFFF2A3AC0FF1117485E} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000300000 + 0033000000330000003300000033000000330000003300000033000000330000 + 00330000002F0000000000000000000000000000000000000000A36B31F2AB6E + 31FFAA6B2DFFAA6C2FFFAA6D31FFAA6D31FFAA6D31FFAA6C2FFFAA6B2DFFAB6E + 31FFA26A31F10000000000000000000000000000000000000000AB6E31FFEEAF + 60FFFFF7DEFFE9A95BFFEAAB5EFFEAAB5FFFEAAB5EFFE9A95AFFFFF7DEFFEEAF + 60FFAB6E30FF0000003300000033000000330000003300000023AA6B2DFFFFF6 + DDFFFCEDD2FFFCEFD5FFFDF1D9FFFDF0D7FFFCEED4FFFCEDD2FFFCEDD1FFFFF6 + DDFFA8692BFFAB6E31FFAA6C2FFFAA6B2DFFAB6E2FFF784F24C0AA6C2EFFE8B0 + 68FFFDEED4FFDD9C4FFFDEA054FFDD9E52FFFDEFD6FFDA9747FFFCEDD1FFE7AE + 67FFA56628FFEEAF62FFEAA95BFFFFF6DCFFF4C180FFAB6D2FFFAA6C2FFFE7B4 + 72FFFEF1D8FFD99C52FFDBA159FFDBA058FFD99C51FFFDEFD5FFFCEDD2FFE6B2 + 6FFFA36324FFFFF5DCFFFCEDD2FFFCECCFFFFFF5DAFFA96B2CFFAA6C2FFFE8B7 + 78FFFEF1D8FFD59A53FFD89F5AFFD89F5BFFD69C56FFDDAD70FFFEEFD5FFE7B5 + 76FFA46324FFFFF4DBFFE2AC68FFFBEBCEFFF2CC98FFAA6B2CFFAA6C2FFFE7B9 + 7FFFFFF1D7FFD0954FFFD39B57FFD39B58FFD29A55FFD0954EFFFFF3DAFFE6B9 + 7FFFA56627FFDDA054FFFDF0D6FFFCECD1FFEEC189FFAA6B2DFFAA6C2EFFE7BB + 84FFFEF0D5FFCA8F4AFFCD9552FFD7A668FFE8BF8BFFE8BE89FFE6BC86FFE9BF + 8AFFA6672AFFDAA05AFFD4974EFFFDEFD4FFEEC590FFAA6B2DFFAA6C2EFFE5BD + 8AFFFDEED2FFFFF1D6FFC58D48FFE6C08EFFBF894FFFA66728FFA56426FFE9C5 + 94FFA6672AFFD59D58FFD1964FFFFFF2D9FFEECA96FFAA6C2EFFAA6B2CFFE5C0 + 8EFFFDECD0FFBB813AFFFFF0D5FFECCDA3FFA56526FFDBA25AFFE8C89CFFCFA2 + 6EFFBF894DFFE9C18DFFE8BF8AFFE6BC86FFE9C08AFFAB6D2FFFAA6B2BFFFFF5 + DAFFFDECCEFFFDECCFFFFEEED2FFEBCEA6FFA56322FFE8CAA0FFCEA16EFFB173 + 31FFE8C390FFC08950FFA66728FFA56426FFEBC695FFAC6E30FFAB6D2EFFE6CB + A3FFFFF5D9FFE2C59AFFE3C69DFFE5C9A1FFE7CCA4FFCDA371FFAE6E2AFFFFF4 + DAFFE6C392FFA66627FFDCA35AFFE9CA9EFFD3A773FF754C22AC784E24B0AB6D + 2EFFAA6B2BFFAA6B2BFFAA6B2CFFAA6A2AFFA76524FFC79865FFFFF3D7FFFFF0 + D4FFE4C396FFA56323FFE9CCA2FFD2A775FF70481FA700000000000000000000 + 0000000000000000000000000000AD6F31FFE9D0A9FFFFF9DFFFE3C69CFFE3C7 + 9EFFE6CAA2FFE8CFA7FFD1AA7AFF70471FA70000000000000000000000000000 + 0000000000000000000000000000774E24AFAB6C2EFFC18E58FFA9692AFFAA6B + 2CFFAB6C2EFFAC6D2FFF754B21AC000000000000000000000000} + end> + end +end diff --git a/assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.pas b/assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.pas new file mode 100644 index 0000000..2fa93b7 --- /dev/null +++ b/assets/InstructionEditor/Forms/Editors/formEditorInstructionOperands.pas @@ -0,0 +1,80 @@ +unit formEditorInstructionOperands; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, System.ImageList, Vcl.ImgList, cxGraphics, + Vcl.StdCtrls, untInstructionEditor; + +type + TfrmEditorInstructionOperands = class(TForm) + GroupBox: TGroupBox; + btnCancel: TButton; + btnApply: TButton; + imgIcons16: TcxImageList; + PageControl1: TPageControl; + TabSheet1: TTabSheet; + GroupBox1: TGroupBox; + procedure FormCreate(Sender: TObject); + procedure btnCancelClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure btnApplyClick(Sender: TObject); + strict private + FApplyChanges: Boolean; + FEditor: TInstructionEditor; + FDefinition: TInstructionDefinition; + strict private + function GetOperands: TInstructionOperands; inline; + procedure SetOperands(const Value: TInstructionOperands); inline; + public + property ApplyChanges: Boolean read FApplyChanges; + property Operands: TInstructionOperands read GetOperands write SetOperands; + end; + +var + frmEditorInstructionOperands: TfrmEditorInstructionOperands; + +implementation + +{$R *.dfm} + +{ TfrmEditorInstructionOperands } + +procedure TfrmEditorInstructionOperands.btnApplyClick(Sender: TObject); +begin + FApplyChanges := true; + Close; +end; + +procedure TfrmEditorInstructionOperands.btnCancelClick(Sender: TObject); +begin + Close; +end; + +procedure TfrmEditorInstructionOperands.FormCreate(Sender: TObject); +begin + FEditor := TInstructionEditor.Create; + FEditor.Reset; + FDefinition := FEditor.CreateDefinition('dummy'); +end; + +procedure TfrmEditorInstructionOperands.FormDestroy(Sender: TObject); +begin + if (Assigned(FEditor)) then + begin + FEditor.Free; + end; +end; + +function TfrmEditorInstructionOperands.GetOperands: TInstructionOperands; +begin + Result := FDefinition.Operands; +end; + +procedure TfrmEditorInstructionOperands.SetOperands(const Value: TInstructionOperands); +begin + FDefinition.Operands.Assign(Value); +end; + +end. diff --git a/assets/InstructionEditor/Forms/Editors/formEditorX86Registers.dfm b/assets/InstructionEditor/Forms/Editors/formEditorX86Registers.dfm new file mode 100644 index 0000000..8b036fe --- /dev/null +++ b/assets/InstructionEditor/Forms/Editors/formEditorX86Registers.dfm @@ -0,0 +1,244 @@ +object frmEditorX86Registers: TfrmEditorX86Registers + Left = 0 + Top = 0 + BorderStyle = bsDialog + Caption = 'frmEditorX86Registers' + ClientHeight = 421 + ClientWidth = 594 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object GroupBox: TGroupBox + AlignWithMargins = True + Left = 3 + Top = 379 + Width = 588 + Height = 39 + Margins.Top = 0 + Align = alBottom + TabOrder = 1 + DesignSize = ( + 588 + 39) + object btnCancel: TButton + Left = 468 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Cancel' + ImageIndex = 1 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 1 + OnClick = btnCancelClick + end + object btnApply: TButton + Left = 350 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Apply' + ImageIndex = 0 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 0 + OnClick = btnApplyClick + end + end + object VirtualTreeView: TVirtualStringTree + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 588 + Height = 373 + Align = alClient + Header.AutoSizeIndex = -1 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.MainColumn = -1 + Images = imgTreeView + TabOrder = 0 + TreeOptions.MiscOptions = [toCheckSupport, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick] + TreeOptions.PaintOptions = [toHideFocusRect, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowTreeLines, toShowVertGridLines, toThemeAware, toUseBlendedImages, toFullVertGridLines, toAlwaysHideSelection, toUseBlendedSelection] + TreeOptions.SelectionOptions = [toFullRowSelect] + OnChecked = VirtualTreeViewChecked + OnCollapsing = VirtualTreeViewCollapsing + OnFreeNode = VirtualTreeViewFreeNode + OnGetText = VirtualTreeViewGetText + OnGetImageIndex = VirtualTreeViewGetImageIndex + Columns = <> + end + object imgIcons16: TcxImageList + FormatVersion = 1 + DesignInfo = 2097208 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 20000000000000040000000000000000000000000000000000000000001B0000 + 0033000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000001B00000000004B29A00089 + 49FF008747FF008747FF008747FF008747FF008747FF008747FF008747FF0087 + 47FF008747FF008747FF008747FF008949FF004B29A000000000008949FF07E4 + A4FF07E5A5FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4 + A4FF07E4A4FF07E4A4FF07E5A5FF07E4A4FF008949FF00000000008747FF13E7 + AAFF00DB9BFF00DA9AFF00DA99FF00DA99FF00DA99FF00DA99FF00DA9AFF00DA + 9AFF00DA9AFF00DA9AFF00DB9BFF13E7AAFF008747FF00000000008746FF1EE7 + AEFF00D699FF00D698FF00D495FF00D391FF00D391FF00D495FF00D698FF00D6 + 99FF00D699FF00D699FF00D699FF1EE7AEFF008746FF00000000008746FF2AE9 + B3FF00D299FF00D095FF00CC8DFFFFFFFFFFFFFFFFFF00CC8DFF00D096FF00D2 + 99FF00D29AFF00D29AFF00D29AFF2AE9B3FF008746FF00000000008746FF35EA + B7FF00CD96FF00C98DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C98DFF00CD + 96FF00CF99FF00CF9AFF00CF9AFF36EAB8FF008746FF00000000008745FF3EEB + BBFF00C692FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C4 + 8DFF00C995FF00CA99FF00CA99FF41ECBDFF008745FF00000000008745FF4BED + BFFF00C393FFFFFFFFFFFFFFFFFF00C08BFF00BF8BFFFFFFFFFFFFFFFFFFFFFF + FFFF00C18EFF00C596FF00C699FF4DEDC0FF008745FF00000000008744FF58EF + C5FF00C197FF00BF93FF00C093FF00C196FF00C196FF00BC8EFFFFFFFFFFFFFF + FFFFFFFFFFFF00BC8EFF00C096FF58EFC5FF008744FF00000000008744FF64F1 + CAFF00BE99FF00C099FF00C09AFF00C19BFF00C19AFF00BE97FF00B98EFFFFFF + FFFFFFFFFFFFFFFFFFFF00BA92FF62F0C9FF008744FF00000000008744FF71F2 + CFFF00BB99FF00BD9AFF00BE9BFF00BE9BFF00BE9BFF00BE9AFF00BB96FF00B6 + 8EFFFFFFFFFFFFFFFFFF00B792FF6EF2CDFF008744FF00000000008744FF7BF4 + D3FF00B699FF00B89AFF00B99BFF00B99BFF00B99BFF00B99BFF00B89AFF00B7 + 98FF00B494FF00B493FF00B496FF7AF3D3FF008744FF00000000008744FF86F5 + D8FF00B198FF00B299FF00B399FF00B399FF00B399FF00B399FF00B399FF00B2 + 99FF00B298FF00B297FF00B197FF86F5D8FF008744FF00000000008946FF8AF2 + D9FF92F6DEFF93F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6 + DDFF94F6DDFF93F6DDFF92F6DEFF8AF2D9FF008946FF00000000004B29880089 + 46FF008743FF008643FF008643FF008643FF008643FF008643FF008643FF0086 + 43FF008643FF008643FF008743FF008946FF004B298800000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0013000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000003300000013000000001117 + 487E2D3CC0FF2B3BBEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3A + BEFF2A3ABEFF2A3ABEFF2A3ABEFF2B3BBEFF2D3CC0FF1117487E000000002D3D + C0FF6172FAFF6A7DFFFF697CFFFF6D80FFFF7485FFFF7788FFFF7788FFFF7788 + FFFF7485FFFF6D7FFFFF697CFFFF6A7DFFFF6172FAFF2D3DC0FF000000002B3B + BEFF6578FFFF5F73FEFF6F80FFFF7989FFFF5868E6FF3E4ED0FF3F4FD1FF3E4E + D0FF5767E5FF7887FEFF6E7FFFFF5F73FEFF6578FFFF2B3BBEFF000000002B3B + BEFF5D72FFFF697BFEFF7785F8FF3949CAFF1E2A89B70B0F303F07091D26070A + 1F552636B9FF5A6FFDFF576CFAFF687AFCFF5D71FFFF2B3BBEFF000000002C3B + BFFF586EFEFF8593FFFF3B4BCAFF0E143F530000000000000000000000332B3A + BDFF5970FEFF546AFBFF7F8FFFFF8290FDFF586EFEFF2C3BBFFF000000002C3C + BFFF536BFCFF707CE7FF1E2A89BB0000000000000000000000332C3BBEFF546B + FBFF5067F8FF8796FFFF3B4BCDFF6D79E5FF536AFCFF2C3CBFFF000000002D3D + BFFF4F68FBFF4E5BD1FF0B0F304500000000000000332C3BBEFF4E66F9FF4A63 + F6FF8C99FFFF2737BCFF07091F314D5AD0FF4F68FBFF2D3DBFFF000000002E3D + C0FF4A64F9FF3446CDFF07091D2D000000332D3CBEFF4862F7FF445DF4FF8E9C + FFFF2838BDFF0000000007091D313446CDFF4A64F9FF2E3DC0FF000000002E3D + C0FF4660F7FF3445CCFF080A20552D3CBEFF445DF4FF3F59F1FF929FFFFF2838 + BDFF00000000000000000C0F305B3445CDFF4660F7FF2E3DC0FF000000002E3E + C0FF405BF3FF384DDBFF2E3CBCFF3E59F2FF3954EFFF95A2FFFF2838BDFF0000 + 00000000000000000011222D8AC5384EDCFF405BF3FF2E3EC0FF000000002F3E + C0FF3955F0FF3951EAFF3954EEFF3651EDFF9AA5FFFF2838BDFF000000080000 + 000D0000002510153F753041C8FF3A53EBFF3A55F0FF2F3EC0FF000000002E3D + BFFF3A54EFFF324DE9FF334DE9FF324DEBFF2A39BCFF070A1F5507091D510B0F + 3065212D8AC52F41C8FF314CE4FF324DEBFF3A54EFFF2E3DBFFF000000002B3B + BFFF6A7DFDFF334EE8FF2643E4FF2642E3FF253CD4FF2739C7FF273AC8FF273A + C8FF263DD5FF2743E5FF2643E6FF334EE9FF6A7DFDFF2B3BBFFF000000002B3B + C0FF929DFCFFAAB3FFFFAAB3FFFFAAB3FFFFAAB4FFFFAAB4FFFFAAB4FFFFAAB4 + FFFFAAB4FFFFAAB3FFFFAAB3FFFFAAB3FFFF929DFCFF2B3BC0FF000000001117 + 485E2A3AC0FF2838BFFF2737BEFF2737BEFF2737BEFF2737BFFF2737BFFF2737 + BFFF2737BEFF2737BEFF2737BEFF2838BFFF2A3AC0FF1117485E} + end> + end + object imgTreeView: TcxImageList + FormatVersion = 1 + DesignInfo = 2097288 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000300000 + 0033000000330000003300000033000000330000003300000033000000330000 + 00330000003300000033000000330000002F00000000000000004190C6F24398 + D2FF4094D0FF3E92CFFF3E92CEFF3F92CEFF3F92CEFF3F92CEFF3F92CEFF3F92 + CEFF3F92CEFF3F92CEFF3F93CFFF3D8BC2F00000000E000000004499D2FF3F94 + D0FFABFBFFFF9BF3FFFF92F1FFFF93F1FFFF93F1FFFF93F1FFFF93F1FFFF93F1 + FFFF93F1FFFF93F1FFFFA6F8FFFF65B8E3FF122A3A5F000000004398D2FF4FA6 + D9FF8EDAF5FFA2EEFFFF82E5FEFF84E5FEFF84E5FEFF85E6FEFF85E6FEFF85E6 + FEFF85E6FEFF84E6FEFF96EBFFFF8CD8F5FF296088B8000000004296D1FF6BBE + E8FF6DBDE6FFBBF2FFFF75DEFDFF77DEFCFF78DEFCFF7BDFFCFF7DDFFCFF7DDF + FCFF7DDFFCFF7CDFFCFF80E0FDFFADF0FFFF4D9DD3FF0000000E4095D0FF8AD7 + F5FF44A1D8FFDDFDFFFFDAFAFFFFDBFAFFFFDEFAFFFF74DCFCFF76DBFAFF75DA + FAFF74DAFAFF74DAFAFF72D9FAFFA1E8FFFF7CBFE6FF12293A5E3E94D0FFABF0 + FFFF449DD6FF368CCBFF368CCBFF368CCBFF378BCBFF5CBEEAFF6FD9FBFF6AD6 + FAFF68D5F9FF67D4F9FF66D4F9FF82DEFCFFAAE0F6FF296088B93D92CFFFB9F4 + FFFF73DBFBFF6BCCF2FF6CCDF3FF6CCEF3FF6DCEF3FF479CD4FF56BAE9FFDAF8 + FFFFD7F6FFFFD6F6FFFFD5F6FFFFD5F7FFFFDBFCFFFF3E94D0FF3C92CFFFC0F3 + FFFF71DAFBFF74DBFBFF75DBFCFF75DBFCFF76DCFCFF73DAFAFF449CD4FF378C + CBFF368CCBFF358CCCFF348DCCFF3890CEFF3D94D0FF3E8CC2EB3B92CFFFCAF6 + FFFF69D5F9FF6CD5F9FF6BD5F9FF69D5F9FF69D5FAFF6AD7FBFF68D4FAFF5EC7 + F1FF5EC7F2FF5DC8F2FFB4E3F8FF3D94D0FF1A3B5269000000003B92CFFFD5F7 + FFFF60D1F9FF61D0F8FFB4EBFDFFD9F6FFFFDAF8FFFFDAF8FFFFDBF9FFFFDCFA + FFFFDCFAFFFFDCFBFFFFE0FFFFFF3E95D0FF0E1F2A33000000003D94D0FFDCFC + FFFFD8F7FFFFD8F7FFFFDBFAFFFF358ECDFF3991CEFF3A92CFFF3A92CFFF3A92 + CFFF3A92CFFF3B92CFFF3D94D0FF3880B1D700000000000000002E6991B03D94 + D0FF3A92CFFF3A92CFFF3D94D0FF367CACD20000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000330000000000000033000000000000003300000000000000330000 + 000000000033000000000000003300000000000000330000000C000000000000 + 0000519DD5FF00000005519DD3FF00000005519DD3FF00000005519DD3FF0000 + 0005519DD3FF00000005519DD3FF000000055299CBFF14253046000000000000 + 002E50A0D9FF0A090947509FD8FF0A090947509FD8FF0A090947509FD8FF0A09 + 0947509FD8FF0A090947509FD8FF0A090947519CD2FF0F1D2755000000005C5B + 5AEC666361FF676462FF676461FF676462FF676461FF676462FF676461FF6764 + 62FF676461FF676462FF676461FF676361FF67625EFF5D5653EA000000006565 + 65FF625F5DFF585554FF585554FF585554FF585554FF585554FF585554FF5855 + 54FF585554FF585654FF595655FF585554FF625E5DFF656564FF000000006565 + 65FF6E6C6BFF52504FFF4F4D4CFF4E4C4BFF4F4C4BFF4F4D4CFF4F4D4CFF4F4D + 4CFF4F4D4CFF514F4EFF545251FF545251FF6F6C6BFF656565FF000000006364 + 64FF7B7A79FF4A4847FFC1C0C0FFB1B0AFFFA7A6A6FF878585FFCECDCDFFAAA9 + A9FF939291FFC5C4C4FF4E4C4AFF504E4DFF7C7C7AFF636464FF000000006363 + 63FF888686FF424040FFDCDCDCFFD5D6D6FFC0C0C0FFBAB9B9FF3A3838FFBBBA + BBFF747373FFCDCCCCFF494747FF4C4A4AFF8A8888FF636363FF000000006262 + 62FF969595FF403E3EFFE1E0E0FFCFCECEFFC5C4C4FFA5A3A3FFA7A7A5FFC9C8 + C8FFBEBDBDFF9F9F9FFF484646FF484646FF979696FF626262FF000000006161 + 61FFA5A4A4FF3E3C3CFF3D3B3BFF3C3A3AFF3C3A3AFF3E3C3CFF3F3D3DFF3E3B + 3BFF3E3C3CFF413F3FFF444242FF423F3FFFA5A4A4FF616161FF000000006161 + 61FFB5B4B3FF8D8B8AFF8F8C8CFF8F8C8CFF8F8C8CFF8F8D8CFF8F8D8CFF8F8D + 8CFF8F8D8CFF908D8DFF908D8DFF8E8B8BFFB5B4B3FF616161FF000000004341 + 40AA625F5CFF625F5CFF635F5DFF63605DFF63605DFF63605DFF63605DFF6360 + 5DFF63605DFF63605DFF635F5DFF625F5CFF625F5CFF535150D4000000000000 + 0000509CD3FF00000000509CD3FF00000000509CD3FF00000000509CD3FF0000 + 0000509CD3FF00000000509CD3FF00000000509CD3FF00000000000000000000 + 00005397C8FF000000005397C7FF000000005397C7FF000000005397C7FF0000 + 00005397C7FF000000005397C7FF000000005397C8FF00000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end> + end +end diff --git a/assets/InstructionEditor/Forms/Editors/formEditorX86Registers.pas b/assets/InstructionEditor/Forms/Editors/formEditorX86Registers.pas new file mode 100644 index 0000000..5df9297 --- /dev/null +++ b/assets/InstructionEditor/Forms/Editors/formEditorX86Registers.pas @@ -0,0 +1,263 @@ +unit formEditorX86Registers; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, untInstructionEditor, System.ImageList, Vcl.ImgList, + cxGraphics, Vcl.StdCtrls, VirtualTrees; + +type + TfrmEditorX86Registers = class(TForm) + GroupBox: TGroupBox; + btnCancel: TButton; + btnApply: TButton; + imgIcons16: TcxImageList; + VirtualTreeView: TVirtualStringTree; + imgTreeView: TcxImageList; + procedure btnApplyClick(Sender: TObject); + procedure btnCancelClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure VirtualTreeViewFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure VirtualTreeViewGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); + procedure VirtualTreeViewGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + procedure VirtualTreeViewCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; + var Allowed: Boolean); + procedure VirtualTreeViewChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); + strict private + FApplyChanges: Boolean; + FRegisters: TX86RegisterSet; + strict private + procedure SetRegisters(const Value: TX86RegisterSet); + public + property ApplyChanges: Boolean read FApplyChanges; + property Registers: TX86RegisterSet read FRegisters write SetRegisters; + end; + +var + frmEditorX86Registers: TfrmEditorX86Registers; + +implementation + +uses + System.TypInfo; + +{$R *.dfm} + +type + PNodeData = ^TNodeData; + TNodeData = record + public + IsCategory: Boolean; + Text: String; + Value: TX86Register; + end; + +{ TfrmEditorX86Registers } + +procedure TfrmEditorX86Registers.btnApplyClick(Sender: TObject); +begin + FApplyChanges := true; + Close; +end; + +procedure TfrmEditorX86Registers.btnCancelClick(Sender: TObject); +begin + Close; +end; + +procedure TfrmEditorX86Registers.FormCreate(Sender: TObject); + +function AddCategory(Root: PVirtualNode; const Text: String): PVirtualNode; +var + NodeData: PNodeData; +begin + Result := VirtualTreeView.AddChild(Root); + if (Assigned(Root)) then + begin + VirtualTreeView.CheckType[Result] := ctTriStateCheckBox; + end; + NodeData := VirtualTreeView.GetNodeData(Result); + NodeData^.IsCategory := true; + NodeData^.Text := Text; +end; + +procedure AddRegisterRange(Root: PVirtualNode; RegisterLow, RegisterHigh: TX86Register); +var + Node: PVirtualNode; + NodeData: PNodeData; + R: TX86Register; + S: String; +begin + for R := RegisterLow to RegisterHigh do + begin + Node := VirtualTreeView.AddChild(Root); + VirtualTreeView.CheckType[Node] := ctTriStateCheckBox; + NodeData := VirtualTreeView.GetNodeData(Node); + NodeData^.IsCategory := false; + S := GetEnumName(TypeInfo(TX86Register), Ord(R)); + Delete(S, 1, 3); + NodeData^.Text := S; + NodeData^.Value := R; + end; +end; + +var + Root, N1, N2: PVirtualNode; +begin + FRegisters := []; + VirtualTreeView.NodeDataSize := SizeOf(TNodeData); + VirtualTreeView.BeginUpdate; + try + Root := AddCategory(nil, 'Root'); + + N1 := AddCategory(Root, 'General Purpose Registers'); + N2 := AddCategory(N1, '64-bit'); + AddRegisterRange(N2, regRAX, regR15); + N2 := AddCategory(N1, '32-bit'); + AddRegisterRange(N2, regEAX, regR15D); + N2 := AddCategory(N1, '16-bit'); + AddRegisterRange(N2, regAX, regR15W); + N2 := AddCategory(N1, '8-bit'); + AddRegisterRange(N2, regAL, regR15B); + VirtualTreeView.Expanded[N1] := true; + + N1 := AddCategory(Root, 'Floating Point Registers'); + N2 := AddCategory(N1, 'Legacy'); + AddRegisterRange(N2, regST0, regST7); + N2 := AddCategory(N1, 'Multimedia'); + AddRegisterRange(N2, regMM0, regMM7); + VirtualTreeView.Expanded[N1] := true; + + N1 := AddCategory(Root, 'Vector Registers'); + N2 := AddCategory(N1, '512-bit'); + AddRegisterRange(N2, regZMM0, regZMM31); + N2 := AddCategory(N1, '256-bit'); + AddRegisterRange(N2, regYMM0, regYMM31); + N2 := AddCategory(N1, '128-bit'); + AddRegisterRange(N2, regXMM0, regXMM31); + VirtualTreeView.Expanded[N1] := true; + + N1 := AddCategory(Root, 'Special Registers'); + AddRegisterRange(N1, regRFLAGS, regIP); + + N1 := AddCategory(Root, 'Segment Registers'); + AddRegisterRange(N1, regES, regFS); + + N1 := AddCategory(Root, 'Control Registers'); + AddRegisterRange(N1, regCR0, regCR15); + + N1 := AddCategory(Root, 'Debug Registers'); + AddRegisterRange(N1, regDR0, regDR15); + + N1 := AddCategory(Root, 'Mask Registers'); + AddRegisterRange(N1, regK0, regK7); + + N1 := AddCategory(Root, 'Bound Registers'); + AddRegisterRange(N1, regBND0, regBND3); + + VirtualTreeView.Expanded[Root] := true; + finally + VirtualTreeView.EndUpdate; + end; +end; + +procedure TfrmEditorX86Registers.VirtualTreeViewChecked(Sender: TBaseVirtualTree; Node: PVirtualNode); +var + NodeData: PNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData) and (not NodeData^.IsCategory)) then + begin + case Sender.CheckState[Node] of + csCheckedNormal: + FRegisters := FRegisters + [NodeData^.Value]; + csUncheckedNormal: + FRegisters := FRegisters - [NodeData^.Value]; + end; + end; +end; + +procedure TfrmEditorX86Registers.VirtualTreeViewCollapsing(Sender: TBaseVirtualTree; + Node: PVirtualNode; var Allowed: Boolean); +begin + Allowed := (Node.Parent <> Sender.RootNode); +end; + +procedure TfrmEditorX86Registers.VirtualTreeViewFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); +var + NodeData: PNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + Finalize(NodeData^); + end; +end; + +procedure TfrmEditorX86Registers.VirtualTreeViewGetImageIndex(Sender: TBaseVirtualTree; + Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; + var ImageIndex: Integer); +var + NodeData: PNodeData; +begin + ImageIndex := -1; + if (Kind in [ikNormal, ikSelected]) then + begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + if (NodeData^.IsCategory) then + begin + ImageIndex := 0; + end else + begin + ImageIndex := 1; + end; + end; + end; +end; + +procedure TfrmEditorX86Registers.VirtualTreeViewGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + NodeData: PNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + CellText := NodeData^.Text; + end; +end; + +procedure TfrmEditorX86Registers.SetRegisters(const Value: TX86RegisterSet); +var + Node: PVirtualNode; + NodeData: PNodeData; +begin + VirtualTreeView.BeginUpdate; + try + Node := VirtualTreeView.GetFirst; + while (Assigned(Node)) do + begin + NodeData := VirtualTreeView.GetNodeData(Node); + if (Assigned(NodeData) and (not NodeData^.IsCategory)) then + begin + if (NodeData^.Value in Value) then + begin + VirtualTreeView.CheckState[Node] := csCheckedNormal; + end else + begin + VirtualTreeView.CheckState[Node] := csUncheckedNormal; + end; + end; + Node := VirtualTreeView.GetNext(Node); + end; + finally + VirtualTreeView.EndUpdate; + end; +end; + +end. diff --git a/assets/InstructionEditor/Forms/formCreateDefinition.dfm b/assets/InstructionEditor/Forms/formCreateDefinition.dfm new file mode 100644 index 0000000..d2fc7c0 --- /dev/null +++ b/assets/InstructionEditor/Forms/formCreateDefinition.dfm @@ -0,0 +1,157 @@ +object frmCreateDefinition: TfrmCreateDefinition + Left = 0 + Top = 0 + BorderStyle = bsDialog + Caption = 'Create Definition' + ClientHeight = 446 + ClientWidth = 644 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object GroupBox: TGroupBox + AlignWithMargins = True + Left = 3 + Top = 404 + Width = 638 + Height = 39 + Margins.Top = 0 + Align = alBottom + TabOrder = 1 + DesignSize = ( + 638 + 39) + object btnCancel: TButton + Left = 518 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Cancel' + ImageIndex = 1 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 1 + OnClick = btnCancelClick + end + object btnApply: TButton + Left = 400 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Ok' + ImageIndex = 0 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 0 + OnClick = btnApplyClick + end + end + object Inspector: TcxRTTIInspector + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 638 + Height = 398 + Align = alClient + Constraints.MinWidth = 350 + InspectedObject = Owner + LookAndFeel.NativeStyle = True + OptionsView.PaintStyle = psdotNet + OptionsView.RowHeaderMinWidth = 120 + OptionsView.RowHeaderWidth = 260 + OptionsView.ShowReadOnlyProperties = True + OptionsView.Sorted = False + TabOrder = 0 + Version = 1 + end + object imgIcons16: TcxImageList + FormatVersion = 1 + DesignInfo = 2097208 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 20000000000000040000000000000000000000000000000000000000001B0000 + 0033000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000001B00000000004B29A00089 + 49FF008747FF008747FF008747FF008747FF008747FF008747FF008747FF0087 + 47FF008747FF008747FF008747FF008949FF004B29A000000000008949FF07E4 + A4FF07E5A5FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4 + A4FF07E4A4FF07E4A4FF07E5A5FF07E4A4FF008949FF00000000008747FF13E7 + AAFF00DB9BFF00DA9AFF00DA99FF00DA99FF00DA99FF00DA99FF00DA9AFF00DA + 9AFF00DA9AFF00DA9AFF00DB9BFF13E7AAFF008747FF00000000008746FF1EE7 + AEFF00D699FF00D698FF00D495FF00D391FF00D391FF00D495FF00D698FF00D6 + 99FF00D699FF00D699FF00D699FF1EE7AEFF008746FF00000000008746FF2AE9 + B3FF00D299FF00D095FF00CC8DFFFFFFFFFFFFFFFFFF00CC8DFF00D096FF00D2 + 99FF00D29AFF00D29AFF00D29AFF2AE9B3FF008746FF00000000008746FF35EA + B7FF00CD96FF00C98DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C98DFF00CD + 96FF00CF99FF00CF9AFF00CF9AFF36EAB8FF008746FF00000000008745FF3EEB + BBFF00C692FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C4 + 8DFF00C995FF00CA99FF00CA99FF41ECBDFF008745FF00000000008745FF4BED + BFFF00C393FFFFFFFFFFFFFFFFFF00C08BFF00BF8BFFFFFFFFFFFFFFFFFFFFFF + FFFF00C18EFF00C596FF00C699FF4DEDC0FF008745FF00000000008744FF58EF + C5FF00C197FF00BF93FF00C093FF00C196FF00C196FF00BC8EFFFFFFFFFFFFFF + FFFFFFFFFFFF00BC8EFF00C096FF58EFC5FF008744FF00000000008744FF64F1 + CAFF00BE99FF00C099FF00C09AFF00C19BFF00C19AFF00BE97FF00B98EFFFFFF + FFFFFFFFFFFFFFFFFFFF00BA92FF62F0C9FF008744FF00000000008744FF71F2 + CFFF00BB99FF00BD9AFF00BE9BFF00BE9BFF00BE9BFF00BE9AFF00BB96FF00B6 + 8EFFFFFFFFFFFFFFFFFF00B792FF6EF2CDFF008744FF00000000008744FF7BF4 + D3FF00B699FF00B89AFF00B99BFF00B99BFF00B99BFF00B99BFF00B89AFF00B7 + 98FF00B494FF00B493FF00B496FF7AF3D3FF008744FF00000000008744FF86F5 + D8FF00B198FF00B299FF00B399FF00B399FF00B399FF00B399FF00B399FF00B2 + 99FF00B298FF00B297FF00B197FF86F5D8FF008744FF00000000008946FF8AF2 + D9FF92F6DEFF93F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6 + DDFF94F6DDFF93F6DDFF92F6DEFF8AF2D9FF008946FF00000000004B29880089 + 46FF008743FF008643FF008643FF008643FF008643FF008643FF008643FF0086 + 43FF008643FF008643FF008743FF008946FF004B298800000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0013000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000003300000013000000001117 + 487E2D3CC0FF2B3BBEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3A + BEFF2A3ABEFF2A3ABEFF2A3ABEFF2B3BBEFF2D3CC0FF1117487E000000002D3D + C0FF6172FAFF6A7DFFFF697CFFFF6D80FFFF7485FFFF7788FFFF7788FFFF7788 + FFFF7485FFFF6D7FFFFF697CFFFF6A7DFFFF6172FAFF2D3DC0FF000000002B3B + BEFF6578FFFF5F73FEFF6F80FFFF7989FFFF5868E6FF3E4ED0FF3F4FD1FF3E4E + D0FF5767E5FF7887FEFF6E7FFFFF5F73FEFF6578FFFF2B3BBEFF000000002B3B + BEFF5D72FFFF697BFEFF7785F8FF3949CAFF1E2A89B70B0F303F07091D26070A + 1F552636B9FF5A6FFDFF576CFAFF687AFCFF5D71FFFF2B3BBEFF000000002C3B + BFFF586EFEFF8593FFFF3B4BCAFF0E143F530000000000000000000000332B3A + BDFF5970FEFF546AFBFF7F8FFFFF8290FDFF586EFEFF2C3BBFFF000000002C3C + BFFF536BFCFF707CE7FF1E2A89BB0000000000000000000000332C3BBEFF546B + FBFF5067F8FF8796FFFF3B4BCDFF6D79E5FF536AFCFF2C3CBFFF000000002D3D + BFFF4F68FBFF4E5BD1FF0B0F304500000000000000332C3BBEFF4E66F9FF4A63 + F6FF8C99FFFF2737BCFF07091F314D5AD0FF4F68FBFF2D3DBFFF000000002E3D + C0FF4A64F9FF3446CDFF07091D2D000000332D3CBEFF4862F7FF445DF4FF8E9C + FFFF2838BDFF0000000007091D313446CDFF4A64F9FF2E3DC0FF000000002E3D + C0FF4660F7FF3445CCFF080A20552D3CBEFF445DF4FF3F59F1FF929FFFFF2838 + BDFF00000000000000000C0F305B3445CDFF4660F7FF2E3DC0FF000000002E3E + C0FF405BF3FF384DDBFF2E3CBCFF3E59F2FF3954EFFF95A2FFFF2838BDFF0000 + 00000000000000000011222D8AC5384EDCFF405BF3FF2E3EC0FF000000002F3E + C0FF3955F0FF3951EAFF3954EEFF3651EDFF9AA5FFFF2838BDFF000000080000 + 000D0000002510153F753041C8FF3A53EBFF3A55F0FF2F3EC0FF000000002E3D + BFFF3A54EFFF324DE9FF334DE9FF324DEBFF2A39BCFF070A1F5507091D510B0F + 3065212D8AC52F41C8FF314CE4FF324DEBFF3A54EFFF2E3DBFFF000000002B3B + BFFF6A7DFDFF334EE8FF2643E4FF2642E3FF253CD4FF2739C7FF273AC8FF273A + C8FF263DD5FF2743E5FF2643E6FF334EE9FF6A7DFDFF2B3BBFFF000000002B3B + C0FF929DFCFFAAB3FFFFAAB3FFFFAAB3FFFFAAB4FFFFAAB4FFFFAAB4FFFFAAB4 + FFFFAAB4FFFFAAB3FFFFAAB3FFFFAAB3FFFF929DFCFF2B3BC0FF000000001117 + 485E2A3AC0FF2838BFFF2737BEFF2737BEFF2737BEFF2737BFFF2737BFFF2737 + BFFF2737BEFF2737BEFF2737BEFF2838BFFF2A3AC0FF1117485E} + end> + end +end diff --git a/assets/InstructionEditor/Forms/formCreateDefinition.pas b/assets/InstructionEditor/Forms/formCreateDefinition.pas new file mode 100644 index 0000000..8db6b3b --- /dev/null +++ b/assets/InstructionEditor/Forms/formCreateDefinition.pas @@ -0,0 +1,50 @@ +unit formCreateDefinition; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, cxGraphics, cxControls, cxLookAndFeels, + cxLookAndFeelPainters, cxStyles, cxEdit, dxSkinsCore, cxInplaceContainer, cxVGrid, cxOI, + System.ImageList, Vcl.ImgList, Vcl.StdCtrls, dxSkinSeven; + +type + TfrmCreateDefinition = class(TForm) + GroupBox: TGroupBox; + btnCancel: TButton; + btnApply: TButton; + imgIcons16: TcxImageList; + Inspector: TcxRTTIInspector; + procedure btnCancelClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure btnApplyClick(Sender: TObject); + private + FCanceled: Boolean; + public + property Canceled: Boolean read FCanceled; + end; + +var + frmCreateDefinition: TfrmCreateDefinition; + +implementation + +{$R *.dfm} + +procedure TfrmCreateDefinition.btnApplyClick(Sender: TObject); +begin + Close; +end; + +procedure TfrmCreateDefinition.btnCancelClick(Sender: TObject); +begin + FCanceled := true; + Close; +end; + +procedure TfrmCreateDefinition.FormCreate(Sender: TObject); +begin + FCanceled := false; +end; + +end. diff --git a/assets/InstructionEditor/Forms/formGenerator.dfm b/assets/InstructionEditor/Forms/formGenerator.dfm new file mode 100644 index 0000000..fc03640 --- /dev/null +++ b/assets/InstructionEditor/Forms/formGenerator.dfm @@ -0,0 +1,329 @@ +object frmGenerator: TfrmGenerator + Left = 0 + Top = 0 + BorderStyle = bsDialog + Caption = 'Generator' + ClientHeight = 371 + ClientWidth = 544 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object GroupBox: TGroupBox + AlignWithMargins = True + Left = 3 + Top = 329 + Width = 538 + Height = 39 + Margins.Top = 0 + Align = alBottom + TabOrder = 0 + DesignSize = ( + 538 + 39) + object btnClose: TButton + Left = 418 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Close' + ImageIndex = 1 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 1 + OnClick = btnCloseClick + end + object btnGenerate: TButton + Left = 300 + Top = 7 + Width = 112 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'Generate' + ImageIndex = 0 + ImageMargins.Left = 2 + ImageMargins.Top = 2 + Images = imgIcons16 + TabOrder = 0 + OnClick = btnGenerateClick + end + end + object TreeView: TVirtualStringTree + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 538 + Height = 323 + Align = alClient + Header.AutoSizeIndex = 0 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.Options = [hoAutoResize, hoVisible] + Images = imgTreeView + TabOrder = 1 + TreeOptions.MiscOptions = [toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick] + TreeOptions.PaintOptions = [toHideFocusRect, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowTreeLines, toShowVertGridLines, toThemeAware, toUseBlendedImages, toFullVertGridLines, toAlwaysHideSelection, toUseBlendedSelection] + TreeOptions.SelectionOptions = [toFullRowSelect] + OnBeforeCellPaint = TreeViewBeforeCellPaint + OnCollapsing = TreeViewCollapsing + OnFreeNode = TreeViewFreeNode + OnGetText = TreeViewGetText + OnGetImageIndex = TreeViewGetImageIndex + Columns = < + item + Position = 0 + Width = 384 + WideText = 'Name' + end + item + Alignment = taRightJustify + Position = 1 + Width = 150 + WideText = 'Value' + end> + end + object imgIcons16: TcxImageList + FormatVersion = 1 + DesignInfo = 2097208 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 20000000000000040000000000000000000000000000000000000000001B0000 + 0033000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000001B00000000004B29A00089 + 49FF008747FF008747FF008747FF008747FF008747FF008747FF008747FF0087 + 47FF008747FF008747FF008747FF008949FF004B29A000000000008949FF07E4 + A4FF07E5A5FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4A4FF07E4 + A4FF07E4A4FF07E4A4FF07E5A5FF07E4A4FF008949FF00000000008747FF13E7 + AAFF00DB9BFF00DA9AFF00DA99FF00DA99FF00DA99FF00DA99FF00DA9AFF00DA + 9AFF00DA9AFF00DA9AFF00DB9BFF13E7AAFF008747FF00000000008746FF1EE7 + AEFF00D699FF00D698FF00D495FF00D391FF00D391FF00D495FF00D698FF00D6 + 99FF00D699FF00D699FF00D699FF1EE7AEFF008746FF00000000008746FF2AE9 + B3FF00D299FF00D095FF00CC8DFFFFFFFFFFFFFFFFFF00CC8DFF00D096FF00D2 + 99FF00D29AFF00D29AFF00D29AFF2AE9B3FF008746FF00000000008746FF35EA + B7FF00CD96FF00C98DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C98DFF00CD + 96FF00CF99FF00CF9AFF00CF9AFF36EAB8FF008746FF00000000008745FF3EEB + BBFF00C692FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C4 + 8DFF00C995FF00CA99FF00CA99FF41ECBDFF008745FF00000000008745FF4BED + BFFF00C393FFFFFFFFFFFFFFFFFF00C08BFF00BF8BFFFFFFFFFFFFFFFFFFFFFF + FFFF00C18EFF00C596FF00C699FF4DEDC0FF008745FF00000000008744FF58EF + C5FF00C197FF00BF93FF00C093FF00C196FF00C196FF00BC8EFFFFFFFFFFFFFF + FFFFFFFFFFFF00BC8EFF00C096FF58EFC5FF008744FF00000000008744FF64F1 + CAFF00BE99FF00C099FF00C09AFF00C19BFF00C19AFF00BE97FF00B98EFFFFFF + FFFFFFFFFFFFFFFFFFFF00BA92FF62F0C9FF008744FF00000000008744FF71F2 + CFFF00BB99FF00BD9AFF00BE9BFF00BE9BFF00BE9BFF00BE9AFF00BB96FF00B6 + 8EFFFFFFFFFFFFFFFFFF00B792FF6EF2CDFF008744FF00000000008744FF7BF4 + D3FF00B699FF00B89AFF00B99BFF00B99BFF00B99BFF00B99BFF00B89AFF00B7 + 98FF00B494FF00B493FF00B496FF7AF3D3FF008744FF00000000008744FF86F5 + D8FF00B198FF00B299FF00B399FF00B399FF00B399FF00B399FF00B399FF00B2 + 99FF00B298FF00B297FF00B197FF86F5D8FF008744FF00000000008946FF8AF2 + D9FF92F6DEFF93F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6DDFF94F6 + DDFF94F6DDFF93F6DDFF92F6DEFF8AF2D9FF008946FF00000000004B29880089 + 46FF008743FF008643FF008643FF008643FF008643FF008643FF008643FF0086 + 43FF008643FF008643FF008743FF008946FF004B298800000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0013000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000330000003300000013000000001117 + 487E2D3CC0FF2B3BBEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3ABEFF2A3A + BEFF2A3ABEFF2A3ABEFF2A3ABEFF2B3BBEFF2D3CC0FF1117487E000000002D3D + C0FF6172FAFF6A7DFFFF697CFFFF6D80FFFF7485FFFF7788FFFF7788FFFF7788 + FFFF7485FFFF6D7FFFFF697CFFFF6A7DFFFF6172FAFF2D3DC0FF000000002B3B + BEFF6578FFFF5F73FEFF6F80FFFF7989FFFF5868E6FF3E4ED0FF3F4FD1FF3E4E + D0FF5767E5FF7887FEFF6E7FFFFF5F73FEFF6578FFFF2B3BBEFF000000002B3B + BEFF5D72FFFF697BFEFF7785F8FF3949CAFF1E2A89B70B0F303F07091D26070A + 1F552636B9FF5A6FFDFF576CFAFF687AFCFF5D71FFFF2B3BBEFF000000002C3B + BFFF586EFEFF8593FFFF3B4BCAFF0E143F530000000000000000000000332B3A + BDFF5970FEFF546AFBFF7F8FFFFF8290FDFF586EFEFF2C3BBFFF000000002C3C + BFFF536BFCFF707CE7FF1E2A89BB0000000000000000000000332C3BBEFF546B + FBFF5067F8FF8796FFFF3B4BCDFF6D79E5FF536AFCFF2C3CBFFF000000002D3D + BFFF4F68FBFF4E5BD1FF0B0F304500000000000000332C3BBEFF4E66F9FF4A63 + F6FF8C99FFFF2737BCFF07091F314D5AD0FF4F68FBFF2D3DBFFF000000002E3D + C0FF4A64F9FF3446CDFF07091D2D000000332D3CBEFF4862F7FF445DF4FF8E9C + FFFF2838BDFF0000000007091D313446CDFF4A64F9FF2E3DC0FF000000002E3D + C0FF4660F7FF3445CCFF080A20552D3CBEFF445DF4FF3F59F1FF929FFFFF2838 + BDFF00000000000000000C0F305B3445CDFF4660F7FF2E3DC0FF000000002E3E + C0FF405BF3FF384DDBFF2E3CBCFF3E59F2FF3954EFFF95A2FFFF2838BDFF0000 + 00000000000000000011222D8AC5384EDCFF405BF3FF2E3EC0FF000000002F3E + C0FF3955F0FF3951EAFF3954EEFF3651EDFF9AA5FFFF2838BDFF000000080000 + 000D0000002510153F753041C8FF3A53EBFF3A55F0FF2F3EC0FF000000002E3D + BFFF3A54EFFF324DE9FF334DE9FF324DEBFF2A39BCFF070A1F5507091D510B0F + 3065212D8AC52F41C8FF314CE4FF324DEBFF3A54EFFF2E3DBFFF000000002B3B + BFFF6A7DFDFF334EE8FF2643E4FF2642E3FF253CD4FF2739C7FF273AC8FF273A + C8FF263DD5FF2743E5FF2643E6FF334EE9FF6A7DFDFF2B3BBFFF000000002B3B + C0FF929DFCFFAAB3FFFFAAB3FFFFAAB3FFFFAAB4FFFFAAB4FFFFAAB4FFFFAAB4 + FFFFAAB4FFFFAAB3FFFFAAB3FFFFAAB3FFFF929DFCFF2B3BC0FF000000001117 + 485E2A3AC0FF2838BFFF2737BEFF2737BEFF2737BEFF2737BFFF2737BFFF2737 + BFFF2737BEFF2737BEFF2737BEFF2838BFFF2A3AC0FF1117485E} + end> + end + object imgTreeView: TcxImageList + FormatVersion = 1 + DesignInfo = 2097288 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000300000 + 0033000000330000003300000033000000330000003300000033000000330000 + 00330000003300000033000000330000002F00000000000000004190C6F24398 + D2FF4094D0FF3E92CFFF3E92CEFF3F92CEFF3F92CEFF3F92CEFF3F92CEFF3F92 + CEFF3F92CEFF3F92CEFF3F93CFFF3D8BC2F00000000E000000004499D2FF3F94 + D0FFABFBFFFF9BF3FFFF92F1FFFF93F1FFFF93F1FFFF93F1FFFF93F1FFFF93F1 + FFFF93F1FFFF93F1FFFFA6F8FFFF65B8E3FF122A3A5F000000004398D2FF4FA6 + D9FF8EDAF5FFA2EEFFFF82E5FEFF84E5FEFF84E5FEFF85E6FEFF85E6FEFF85E6 + FEFF85E6FEFF84E6FEFF96EBFFFF8CD8F5FF296088B8000000004296D1FF6BBE + E8FF6DBDE6FFBBF2FFFF75DEFDFF77DEFCFF78DEFCFF7BDFFCFF7DDFFCFF7DDF + FCFF7DDFFCFF7CDFFCFF80E0FDFFADF0FFFF4D9DD3FF0000000E4095D0FF8AD7 + F5FF44A1D8FFDDFDFFFFDAFAFFFFDBFAFFFFDEFAFFFF74DCFCFF76DBFAFF75DA + FAFF74DAFAFF74DAFAFF72D9FAFFA1E8FFFF7CBFE6FF12293A5E3E94D0FFABF0 + FFFF449DD6FF368CCBFF368CCBFF368CCBFF378BCBFF5CBEEAFF6FD9FBFF6AD6 + FAFF68D5F9FF67D4F9FF66D4F9FF82DEFCFFAAE0F6FF296088B93D92CFFFB9F4 + FFFF73DBFBFF6BCCF2FF6CCDF3FF6CCEF3FF6DCEF3FF479CD4FF56BAE9FFDAF8 + FFFFD7F6FFFFD6F6FFFFD5F6FFFFD5F7FFFFDBFCFFFF3E94D0FF3C92CFFFC0F3 + FFFF71DAFBFF74DBFBFF75DBFCFF75DBFCFF76DCFCFF73DAFAFF449CD4FF378C + CBFF368CCBFF358CCCFF348DCCFF3890CEFF3D94D0FF3E8CC2EB3B92CFFFCAF6 + FFFF69D5F9FF6CD5F9FF6BD5F9FF69D5F9FF69D5FAFF6AD7FBFF68D4FAFF5EC7 + F1FF5EC7F2FF5DC8F2FFB4E3F8FF3D94D0FF1A3B5269000000003B92CFFFD5F7 + FFFF60D1F9FF61D0F8FFB4EBFDFFD9F6FFFFDAF8FFFFDAF8FFFFDBF9FFFFDCFA + FFFFDCFAFFFFDCFBFFFFE0FFFFFF3E95D0FF0E1F2A33000000003D94D0FFDCFC + FFFFD8F7FFFFD8F7FFFFDBFAFFFF358ECDFF3991CEFF3A92CFFF3A92CFFF3A92 + CFFF3A92CFFF3B92CFFF3D94D0FF3880B1D700000000000000002E6991B03D94 + D0FF3A92CFFF3A92CFFF3D94D0FF367CACD20000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000001E00000033000000330000 + 00330000001E0000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000001E3B3B3BAC636363FF626263FF6363 + 63FF3B3B3BAC0000001E00000000000000000000000000000000000000000000 + 00000000000000000000000000003B3B3BAC6F6E6EFF95918FFF989492FF9591 + 8FFF6F6E6EFF3B3B3BAC00000000000000000000000000000000000000000000 + 0000000000000000000000000000636464FF858381FF878481FF868381FF8784 + 81FF858381FF636464FF00000000000000000000000000000000000000000000 + 0000000000000000000000000000626363FF8E8C8BFF767371FF767371FF7673 + 71FF8E8C8BFF626363FF00000000000000000000000000000000000000000000 + 0000000000000000000000000000626363FFA7A5A5FF7D7B7AFF787675FF7D7B + 7AFFA7A5A5FF626363FF00000000000000000000000000000000000000000000 + 00000000000000000000000000003B3B3B976F6E6EFFA6A5A4FFA9A8A9FFA6A5 + A4FF6D6E6EFF3B3B3B9700000000000000000000000000000000000000000000 + 0000000000000000000000000000000000003B3B3B97626262FF616161FF6262 + 62FF3B3B3B970000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000024000000330000 + 002F000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000005F31C100C684FF007D + 41ED000000290000000000000000000000000000000000000000000000000000 + 00000000000000000024000000330000003300000033008847FF00E4A6FF00BE + 80FF006F3BD90000002100000000000000000000000000000000000000000000 + 000000000000005F31C1008A48FF008847FF008746FF008847FF00D9A1FF00D8 + A0FF00BC80FF005A2FB900000022000000000000000000000000000000000000 + 000000000000008947FF43E8C2FF00D7A0FF00D7A0FF00D59FFF00D09CFF00D0 + 9CFF00D39FFF00B981FF005D32BD000000000000000000000000000000000000 + 000000000000008845FF6FE8D0FF00C899FF00C899FF00C899FF00C797FF00C8 + 98FF00CA9AFF63E6CDFF008A47FF000000000000000000000000000000000000 + 000000000000008946FF99F0E4FF4FE9D5FF51E8D4FF4DE6D1FF94E8D8FF00C3 + 97FF60E0C7FF00B281FF005E31AC000000000000000000000000000000000000 + 000000000000005F32B2008946FF008744FF008642FF008847FF82E4D6FF5ADA + C4FF00AD7FFF005A2EA700000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000008847FF7AE4D8FF00AA + 7FFF006F39CF0000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000005F32B200AA85FF007E + 40E9000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000001E00000033000000330000 + 00330000001E0000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000001E6E4F11ACB78319FFB68218FFB783 + 19FF6E4F11AC0000001E00000000000000000000000000000000000000000000 + 00000000000000000000000000006E4F11ACC5922FFFEDC074FFF1C47AFFEDC0 + 74FFC5922FFF6E4F11AC00000000000000000000000000000000000000000000 + 0000000000000000000000000000B7841AFFEAB45CFFE9B45CFFE8B35BFFE9B4 + 5CFFEAB45CFFB7841AFF00000000000000000000000000000000000000000000 + 0000000000000000000000000000B78219FFEBB964FFE3A641FFE3A641FFE3A6 + 41FFEBB964FFB78219FF00000000000000000000000000000000000000000000 + 0000000000000000000000000000B78319FFEEC684FFE5AA49FFE2A641FFE5AA + 49FFEEC684FFB78319FF00000000000000000000000000000000000000000000 + 00000000000000000000000000006E4F1197C6922EFFECC482FFEFC886FFECC4 + 82FFC6912CFF6E4F119700000000000000000000000000000000000000000000 + 0000000000000000000000000000000000006E4F1197B78319FFB68217FFB783 + 19FF6E4F11970000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end> + end +end diff --git a/assets/InstructionEditor/Forms/formGenerator.pas b/assets/InstructionEditor/Forms/formGenerator.pas new file mode 100644 index 0000000..5f35205 --- /dev/null +++ b/assets/InstructionEditor/Forms/formGenerator.pas @@ -0,0 +1,395 @@ +unit formGenerator; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VirtualTrees, System.ImageList, Vcl.ImgList, cxGraphics, + Vcl.StdCtrls, untInstructionEditor; + +type + TfrmGenerator = class(TForm) + GroupBox: TGroupBox; + btnClose: TButton; + btnGenerate: TButton; + imgIcons16: TcxImageList; + TreeView: TVirtualStringTree; + imgTreeView: TcxImageList; + procedure FormCreate(Sender: TObject); + procedure TreeViewCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; + var Allowed: Boolean); + procedure TreeViewFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure TreeViewGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); + procedure TreeViewGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType; var CellText: string); + procedure btnCloseClick(Sender: TObject); + procedure btnGenerateClick(Sender: TObject); + procedure TreeViewBeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; + Node: PVirtualNode; Column: TColumnIndex; CellPaintMode: TVTCellPaintMode; CellRect: TRect; + var ContentRect: TRect); + strict private + FEditor: TInstructionEditor; + FProgressNodeTotal: PVirtualNode; + FProgressNode: array[TGeneratorWorkOperation] of PVirtualNode; + FInfoNode: array[0..6] of PVirtualNode; + FCurrentOperation: TGeneratorWorkOperation; + strict private + procedure GeneratorWorkStart(Sender: TObject; Operation: TGeneratorWorkOperation; MinWorkCount, + MaxWorkCount: Integer); + procedure GeneratorWork(Sender: TObject; WorkCount: Integer); + procedure GeneratorWorkEnd(Sender: TObject); + public + property Editor: TInstructionEditor read FEditor write FEditor; + end; + +var + frmGenerator: TfrmGenerator; + +implementation + +uses + untHelperClasses; + +{$R *.dfm} + +type + TGeneratorNodeType = (ntCategory, ntSpacer, ntOperation, ntValue); + + PGeneratorNodeData = ^TGeneratorNodeData; + TGeneratorNodeData = record + public + NodeType: TGeneratorNodeType; + Text: String; + ProgressActive: Boolean; + ProgressMax: Integer; + ProgressUpdateStep: Integer; + Progress: Double; + Value: String; + public + procedure ProgressInit(MaxWorkCount: Integer; MinWorkCount: Integer); inline; + procedure ProgressUpdate(NewProgress: Double); inline; + procedure WorkCountUpdate(WorkCount: Integer); inline; + function IsProgressUpdateStep(WorkCount: Integer): Boolean; inline; + end; + +{ TGeneratorNodeData } + +function TGeneratorNodeData.IsProgressUpdateStep(WorkCount: Integer): Boolean; +begin + Assert(NodeType = ntOperation); + Result := ((WorkCount mod ProgressUpdateStep) = 0); +end; + +procedure TGeneratorNodeData.ProgressInit(MaxWorkCount, MinWorkCount: Integer); +begin + Assert(NodeType = ntOperation); + ProgressMax := (MaxWorkCount - MinWorkCount); + ProgressUpdateStep := Round(ProgressMax / 100) + 1; +end; + +procedure TGeneratorNodeData.ProgressUpdate(NewProgress: Double); +var + Format: TFormatSettings; +begin + Assert(NodeType = ntOperation); + Progress := NewProgress; + Format.DecimalSeparator := '.'; + Value := FormatFloat('0.00', Progress * 100, Format) + '%'; +end; + +procedure TGeneratorNodeData.WorkCountUpdate(WorkCount: Integer); +begin + Assert(NodeType = ntOperation); + if (ProgressMax = 0) then + begin + ProgressUpdate(1.0); + end else + begin + ProgressUpdate(WorkCount / ProgressMax); + end; +end; + +{ TfrmGenerator } + +procedure TfrmGenerator.btnCloseClick(Sender: TObject); +begin + Close; +end; + +procedure TfrmGenerator.btnGenerateClick(Sender: TObject); + +procedure SetInfoNodeValue(Node: PVirtualNode; const Value: String); +var + NodeData: PGeneratorNodeData; +begin + NodeData := TreeView.GetNodeData(Node); + NodeData^.Value := Value; +end; + +var + Generator: TTableGenerator; + Node: PVirtualNode; + NodeData: PGeneratorNodeData; + I: Integer; +begin + TreeView.BeginUpdate; + try + for Node in FProgressNode do + begin + NodeData := TreeView.GetNodeData(Node); + NodeData^.ProgressActive := false; + NodeData^.ProgressUpdate(0); + end; + NodeData := TreeView.GetNodeData(FProgressNodeTotal); + NodeData^.ProgressActive := false; + NodeData^.ProgressUpdate(0); + finally + TreeView.EndUpdate; + end; + Generator := TTableGenerator.Create; + try + Generator.OnWorkStart := GeneratorWorkStart; + Generator.OnWork := GeneratorWork; + Generator.OnWorkEnd := GeneratorWorkEnd; + Generator.GenerateFiles(FEditor, + 'D:\Verteron Development\GitHub\zyan-disassembler-engine old\include\Zydis\Internal\'); + TreeView.BeginUpdate; + try + for I := Low(FInfoNode) to High(FInfoNode) do + begin + case I of + 0: SetInfoNodeValue(FInfoNode[I], IntToStr(Generator.Statistics.FilterCount)); + 1: SetInfoNodeValue(FInfoNode[I], IntToStr(Generator.Statistics.DefinitionCount)); + 2: SetInfoNodeValue(FInfoNode[I], IntToStr(Generator.Statistics.MnemonicCount)); + 3: SetInfoNodeValue(FInfoNode[I], TSizeFormatter.Format(Generator.Statistics.FilterSize)); + 4: SetInfoNodeValue(FInfoNode[I], + TSizeFormatter.Format(Generator.Statistics.DefinitionSize)); + 5: SetInfoNodeValue(FInfoNode[I], + TSizeFormatter.Format(Generator.Statistics.MnemonicSize)); + 6: SetInfoNodeValue(FInfoNode[I], TSizeFormatter.Format(Generator.Statistics.FilterSize + + Generator.Statistics.DefinitionSize + Generator.Statistics.MnemonicSize)); + end; + end; + finally + TreeView.EndUpdate; + end; + Application.ProcessMessages; + finally + Generator.Free; + end; +end; + +procedure TfrmGenerator.FormCreate(Sender: TObject); + +function AddNodeData(Parent: PVirtualNode; NodeType: TGeneratorNodeType; const Text: String; + const Value: String = ''): PVirtualNode; +var + NodeData: PGeneratorNodeData; +begin + Result := TreeView.AddChild(Parent); + NodeData := TreeView.GetNodeData(Result); + if (Assigned(NodeData)) then + begin + NodeData^.NodeType := NodeType; + NodeData^.Text := Text; + case NodeType of + ntOperation: + begin + NodeData^.ProgressActive := false; + NodeData^.ProgressUpdate(0); + end; + ntValue: + NodeData^.Value := Value; + end; + end; +end; + +var + Category: PVirtualNode; +begin + TreeView.NodeDataSize := SizeOf(TGeneratorNodeData); + TreeView.BeginUpdate; + try + Category := AddNodeData(nil, ntCategory, 'Progress'); + FProgressNode[woIndexingDefinitions] := + AddNodeData(Category, ntOperation, 'Indexing definitions'); + FProgressNode[woIndexingFilters] := + AddNodeData(Category, ntOperation, 'Indexing filters'); + FProgressNode[woGeneratingFilterFiles] := + AddNodeData(Category, ntOperation, 'Generating filter files'); + FProgressNode[woGeneratingDefinitionFiles] := + AddNodeData(Category, ntOperation, 'Generating definition files'); + FProgressNode[woGeneratingMnemonicFiles] := + AddNodeData(Category, ntOperation, 'Generating mnemonic files'); + FProgressNodeTotal := + AddNodeData(Category, ntOperation, 'Total progress'); + TreeView.Expanded[Category] := true; + AddNodeData(nil, ntSpacer, ''); + Category := AddNodeData(nil, ntCategory, 'Statistics'); + FInfoNode[0] := AddNodeData(Category, ntValue, 'Filter Count', '-'); + FInfoNode[1] := AddNodeData(Category, ntValue, 'Definition Count', '-'); + FInfoNode[2] := AddNodeData(Category, ntValue, 'Mnemonic Count', '-'); + FInfoNode[3] := AddNodeData(Category, ntValue, 'Filter Size', '-'); + FInfoNode[4] := AddNodeData(Category, ntValue, 'Definition Size', '-'); + FInfoNode[5] := AddNodeData(Category, ntValue, 'MnemonicSize', '-'); + FInfoNode[6] := AddNodeData(Category, ntValue, 'TotalSize', '-'); + TreeView.Expanded[Category] := true; + finally + TreeView.EndUpdate; + end; +end; + +procedure TfrmGenerator.GeneratorWork(Sender: TObject; WorkCount: Integer); +var + Node: PVirtualNode; + NodeData: PGeneratorNodeData; + ProgressTotal: Double; +begin + NodeData := TreeView.GetNodeData(FProgressNode[FCurrentOperation]); + NodeData^.WorkCountUpdate(WorkCount); + if (NodeData^.IsProgressUpdateStep(WorkCount)) then + begin + TreeView.RepaintNode(FProgressNode[FCurrentOperation]); + ProgressTotal := 0; + for Node in FProgressNode do + begin + NodeData := TreeView.GetNodeData(Node); + ProgressTotal := ProgressTotal + NodeData^.Progress; + end; + NodeData := TreeView.GetNodeData(FProgressNodeTotal); + NodeData^.ProgressUpdate(ProgressTotal / Length(FProgressNode)); + TreeView.RepaintNode(FProgressNodeTotal); + Application.ProcessMessages; + end; +end; + +procedure TfrmGenerator.GeneratorWorkEnd(Sender: TObject); +var + NodeData: PGeneratorNodeData; +begin + NodeData := TreeView.GetNodeData(FProgressNode[FCurrentOperation]); + NodeData^.ProgressActive := false; + NodeData^.ProgressUpdate(1); + TreeView.RepaintNode(FProgressNode[FCurrentOperation]); + NodeData := TreeView.GetNodeData(FProgressNodeTotal); + if (FCurrentOperation = High(TGeneratorWorkOperation)) then + begin + NodeData^.ProgressActive := false; + end; + NodeData^.ProgressUpdate((Integer(FCurrentOperation) + 1) / Length(FProgressNode)); + TreeView.RepaintNode(FProgressNodeTotal); +end; + +procedure TfrmGenerator.GeneratorWorkStart(Sender: TObject; Operation: TGeneratorWorkOperation; + MinWorkCount, MaxWorkCount: Integer); +var + NodeData: PGeneratorNodeData; +begin + FCurrentOperation := Operation; + NodeData := TreeView.GetNodeData(FProgressNode[Operation]); + NodeData^.ProgressInit(MaxWorkCount, MinWorkCount); + NodeData^.ProgressActive := true; + TreeView.RepaintNode(FProgressNode[FCurrentOperation]); + NodeData := TreeView.GetNodeData(FProgressNodeTotal); + if (FCurrentOperation = Low(TGeneratorWorkOperation)) then + begin + NodeData^.ProgressActive := true; + TreeView.RepaintNode(FProgressNodeTotal); + end; +end; + + procedure TfrmGenerator.TreeViewBeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; + Node: PVirtualNode; Column: TColumnIndex; CellPaintMode: TVTCellPaintMode; CellRect: TRect; + var ContentRect: TRect); +var + NodeData: PGeneratorNodeData; + ProgressWidth: Integer; +begin + if (Column = 1) then + begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData) and (NodeData^.NodeType = ntOperation) and + (NodeData^.Progress <> 1) and (NodeData^.Progress <> 0)) then + begin + TargetCanvas.Pen.Color := $0000A162; + TargetCanvas.Brush.Color := $0055F2B5; + TargetCanvas.Rectangle(CellRect.Left, CellRect.Top, CellRect.Right, + CellRect.Bottom - CellRect.Top); + TargetCanvas.Pen.Color := $0000A162; + TargetCanvas.Brush.Color := $000FE18E; + ProgressWidth := Round(NodeData^.Progress * (CellRect.Right - CellRect.Left)); + TargetCanvas.Rectangle(CellRect.Left, CellRect.Top, CellRect.Left + ProgressWidth, + CellRect.Bottom - CellRect.Top); + end; + end; +end; + +procedure TfrmGenerator.TreeViewCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; + var Allowed: Boolean); +begin + Allowed := false; +end; + +procedure TfrmGenerator.TreeViewFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); +var + NodeData: PGeneratorNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + Finalize(NodeData^); + end; +end; + +procedure TfrmGenerator.TreeViewGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; + Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); +var + NodeData: PGeneratorNodeData; +begin + ImageIndex := -1; + if (Column = 0) and (Kind in [ikNormal, ikSelected]) then + begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + case NodeData^.NodeType of + ntCategory: + ImageIndex := 0; + ntOperation: + begin + if (NodeData^.ProgressActive) then + begin + ImageIndex := 2; + end else + begin + ImageIndex := 1; + end; + end; + ntValue: + ImageIndex := 3; + end; + end; + end; +end; + +procedure TfrmGenerator.TreeViewGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + NodeData: PGeneratorNodeData; +begin + CellText := ''; + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + if (NodeData^.NodeType in [ntCategory, ntOperation, ntValue]) then + begin + case Column of + 0: CellText := NodeData^.Text; + 1: CellText := NodeData^.Value; + end; + end; + end; +end; + +end. diff --git a/assets/InstructionEditor/Forms/formMain.dfm b/assets/InstructionEditor/Forms/formMain.dfm new file mode 100644 index 0000000..1fb7e43 --- /dev/null +++ b/assets/InstructionEditor/Forms/formMain.dfm @@ -0,0 +1,2508 @@ +object frmMain: TfrmMain + Left = 0 + Top = 0 + Caption = 'InstructionEditor' + ClientHeight = 761 + ClientWidth = 1084 + Color = clBtnFace + Constraints.MinHeight = 800 + Constraints.MinWidth = 1100 + Font.Charset = ANSI_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + OnCloseQuery = FormCloseQuery + OnCreate = FormCreate + OnDestroy = FormDestroy + OnResize = FormResize + PixelsPerInch = 96 + TextHeight = 13 + object Ribbon: TdxRibbon + Left = 0 + Top = 0 + Width = 1084 + Height = 127 + BarManager = BarManager + Style = rs2010 + ColorSchemeAccent = rcsaBlue + ColorSchemeName = 'Blue' + PopupMenuItems = [] + SupportNonClientDrawing = True + Contexts = <> + TabOrder = 1 + TabStop = False + object RibbonTab1: TdxRibbonTab + Active = True + Caption = 'Main Menu' + Groups = < + item + ToolbarName = 'barMainManu' + end + item + ToolbarName = 'barClipboard' + end + item + ToolbarName = 'barEditor' + end + item + ToolbarName = 'barView' + end + item + ToolbarName = 'barGenerator' + end> + Index = 0 + end + end + object StatusBar: TdxRibbonStatusBar + Left = 0 + Top = 736 + Width = 1084 + Height = 25 + Panels = < + item + PanelStyleClassName = 'TdxStatusBarToolbarPanelStyle' + PanelStyle.ToolbarName = 'barStatusBarProgress' + Fixed = False + Width = 300 + end + item + PanelStyleClassName = 'TdxStatusBarToolbarPanelStyle' + PanelStyle.ToolbarName = 'barMnemonicFilter' + Width = 404 + end + item + PanelStyleClassName = 'TdxStatusBarTextPanelStyle' + Text = 'Mnemonics: ' + Width = 100 + end + item + PanelStyleClassName = 'TdxStatusBarTextPanelStyle' + Text = 'Definitions: ' + Width = 100 + end + item + PanelStyleClassName = 'TdxStatusBarTextPanelStyle' + Text = 'Filters: ' + Width = 100 + end> + Ribbon = Ribbon + LookAndFeel.NativeStyle = False + Font.Charset = DEFAULT_CHARSET + Font.Color = clDefault + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + end + object Splitter: TcxSplitter + Left = 364 + Top = 127 + Width = 3 + Height = 609 + end + object pnlInspector: TPanel + Left = 0 + Top = 127 + Width = 364 + Height = 609 + Align = alLeft + BevelOuter = bvNone + TabOrder = 3 + object DockSite: TdxDockSite + Left = 0 + Top = 0 + Width = 364 + Height = 609 + Align = alClient + DockingType = 5 + OriginalWidth = 364 + OriginalHeight = 609 + object LayoutDockSite: TdxLayoutDockSite + Left = 0 + Top = 0 + Width = 364 + Height = 609 + DockingType = 0 + OriginalWidth = 300 + OriginalHeight = 200 + end + object VertContainerDockSite: TdxVertContainerDockSite + Left = 0 + Top = 0 + Width = 364 + Height = 609 + ActiveChildIndex = -1 + AllowFloating = False + AutoHide = False + CaptionButtons = [] + CustomCaptionButtons.Buttons = <> + Dockable = False + DockingType = 0 + OriginalWidth = 185 + OriginalHeight = 140 + object pnlPropertyInspector: TdxDockPanel + Left = 0 + Top = 0 + Width = 364 + Height = 431 + AllowFloating = False + AutoHide = False + Caption = 'Property Inspector' + CaptionButtons = [] + CustomCaptionButtons.Buttons = <> + Dockable = False + ImageIndex = 0 + TabsProperties.CustomButtons.Buttons = <> + DockingType = 2 + OriginalWidth = 350 + OriginalHeight = 431 + object Inspector: TcxRTTIInspector + Left = 0 + Top = 0 + Width = 350 + Height = 396 + Align = alClient + Constraints.MinWidth = 350 + LookAndFeel.NativeStyle = False + LookAndFeel.SkinName = 'Seven' + OptionsView.PaintStyle = psdotNet + OptionsView.RowHeaderMinWidth = 120 + OptionsView.RowHeaderWidth = 170 + OptionsView.ShowReadOnlyProperties = True + OptionsView.Sorted = False + TabOrder = 0 + OnItemChanged = InspectorItemChanged + Version = 1 + end + object Button1: TButton + Left = 279 + Top = -5 + Width = 75 + Height = 25 + Caption = 'Button1' + TabOrder = 1 + OnClick = Button1Click + end + object Button2: TButton + Left = 136 + Top = 104 + Width = 75 + Height = 25 + Caption = 'Button2' + TabOrder = 2 + OnClick = Button2Click + end + end + object pnlPropertyInformation: TdxDockPanel + Left = 0 + Top = 431 + Width = 364 + Height = 178 + AllowFloating = False + AutoHide = False + Caption = 'Property Information' + CaptionButtons = [cbHide] + CustomCaptionButtons.Buttons = <> + Dockable = False + ImageIndex = 1 + TabsProperties.CustomButtons.Buttons = <> + DockingType = 2 + OriginalWidth = 185 + OriginalHeight = 178 + object lblPropertyInfo: TLabel + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 344 + Height = 137 + Align = alClient + Caption = 'No info text available' + Constraints.MinHeight = 120 + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + WordWrap = True + ExplicitWidth = 119 + ExplicitHeight = 120 + end + end + end + end + end + object EditorTree: TVirtualStringTree + Left = 367 + Top = 127 + Width = 717 + Height = 609 + Align = alClient + Header.AutoSizeIndex = 0 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.Options = [hoAutoResize, hoColumnResize, hoShowSortGlyphs, hoVisible] + Images = imgTreeView + TabOrder = 8 + TreeOptions.MiscOptions = [toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning] + TreeOptions.PaintOptions = [toHideFocusRect, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowRoot, toShowTreeLines, toShowVertGridLines, toThemeAware, toUseBlendedImages, toFullVertGridLines, toUseBlendedSelection] + TreeOptions.SelectionOptions = [toExtendedFocus, toFullRowSelect, toRightClickSelect] + TreeOptions.StringOptions = [toSaveCaptions, toShowStaticText, toAutoAcceptEditChange] + OnChange = EditorTreeChange + OnCollapsing = EditorTreeCollapsing + OnCompareNodes = EditorTreeCompareNodes + OnGetText = EditorTreeGetText + OnPaintText = EditorTreePaintText + OnGetImageIndex = EditorTreeGetImageIndex + OnKeyDown = EditorTreeKeyDown + OnMouseUp = EditorTreeMouseUp + Columns = < + item + Position = 0 + Width = 10 + WideText = 'Index' + end + item + Alignment = taRightJustify + Position = 1 + Width = 150 + WideText = 'Opcode' + end + item + Position = 2 + Width = 180 + WideText = 'Mnemonic' + end + item + Position = 3 + Width = 90 + WideText = 'OP A' + end + item + Position = 4 + Width = 90 + WideText = 'OP B' + end + item + Position = 5 + Width = 90 + WideText = 'OP C' + end + item + Position = 6 + Width = 90 + WideText = 'OP D' + end + item + Position = 7 + Width = 150 + WideText = 'Comment' + end> + end + object BarManager: TdxBarManager + AlwaysSaveText = True + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -12 + Font.Name = 'Segoe UI' + Font.Style = [] + Categories.Strings = ( + 'Default') + Categories.ItemsVisibles = ( + 2) + Categories.Visibles = ( + True) + ImageOptions.Images = imgIcons16 + ImageOptions.LargeImages = imgIcons32 + LookAndFeel.NativeStyle = True + LookAndFeel.SkinName = 'Seven' + PopupMenuLinks = <> + UseSystemFont = True + Left = 552 + Top = 176 + DockControlHeights = ( + 0 + 0 + 0 + 0) + object barMainManu: TdxBar + Caption = 'Main Menu' + CaptionButtons = <> + DockedLeft = 0 + DockedTop = 0 + FloatLeft = 1118 + FloatTop = 8 + FloatClientWidth = 0 + FloatClientHeight = 0 + ItemLinks = < + item + Visible = True + ItemName = 'lbLoadDatabase' + end + item + Visible = True + ItemName = 'lbSaveDatabase' + end> + OneOnRow = False + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = False + end + object barEditor: TdxBar + Caption = 'Editor' + CaptionButtons = <> + DockedLeft = 328 + DockedTop = 0 + FloatLeft = 1118 + FloatTop = 8 + FloatClientWidth = 0 + FloatClientHeight = 0 + ItemLinks = < + item + Visible = True + ItemName = 'lbCreateDefinition' + end + item + Distributed = False + Visible = True + ItemName = 'bbDuplicateDefinition' + end + item + Distributed = False + Visible = True + ItemName = 'bbDeleteDefinition' + end> + OneOnRow = False + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = False + end + object barStatusBarProgress: TdxBar + Caption = 'Statusbar Progress' + CaptionButtons = <> + DockedLeft = 0 + DockedTop = 0 + FloatLeft = 1118 + FloatTop = 8 + FloatClientWidth = 0 + FloatClientHeight = 0 + ItemLinks = < + item + ViewLevels = [ivlSmallIconWithText] + Visible = True + ItemName = 'piStatusBarProgress' + end> + OneOnRow = True + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = True + end + object barView: TdxBar + Caption = 'View' + CaptionButtons = <> + DockedLeft = 539 + DockedTop = 0 + FloatLeft = 1118 + FloatTop = 8 + FloatClientWidth = 0 + FloatClientHeight = 0 + ItemLinks = < + item + Visible = True + ItemName = 'lbMnemonicFilter' + end + item + BeginGroup = True + Distributed = False + Visible = True + ItemName = 'bbExpandNodes' + end + item + Distributed = False + Visible = True + ItemName = 'bbCollapseNodes' + end> + OneOnRow = False + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = False + end + object barGenerator: TdxBar + Caption = 'Generator' + CaptionButtons = <> + DockedLeft = 754 + DockedTop = 0 + FloatLeft = 1118 + FloatTop = 8 + FloatClientWidth = 0 + FloatClientHeight = 0 + ItemLinks = < + item + Visible = True + ItemName = 'lbGenerate' + end> + OneOnRow = False + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = False + end + object barClipboard: TdxBar + Caption = 'Clipboard' + CaptionButtons = <> + DockedLeft = 139 + DockedTop = 0 + FloatLeft = 1118 + FloatTop = 8 + FloatClientWidth = 0 + FloatClientHeight = 0 + ItemLinks = < + item + Visible = True + ItemName = 'lbClipboardPaste' + end + item + Distributed = False + Visible = True + ItemName = 'bbClipboardCopy' + end + item + Distributed = False + Visible = True + ItemName = 'bbClipboardCut' + end> + OneOnRow = False + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = False + end + object barMnemonicFilter: TdxBar + AllowClose = False + AllowCustomizing = False + AllowQuickCustomizing = False + Caption = 'Mnemonic Filter' + CaptionButtons = <> + DockedLeft = 0 + DockedTop = 0 + FloatLeft = 823 + FloatTop = 779 + FloatClientWidth = 100 + FloatClientHeight = 46 + ItemLinks = < + item + UserDefine = [udWidth] + UserWidth = 215 + ViewLevels = [ivlSmallIconWithText] + Visible = True + ItemName = 'edtMnemonicFilter' + end + item + UserDefine = [udPaintStyle] + UserPaintStyle = psCaptionGlyph + ViewLevels = [ivlSmallIconWithText] + Visible = True + ItemName = 'bbExactMatch' + end> + NotDocking = [dsNone, dsLeft, dsTop, dsRight, dsBottom] + OneOnRow = True + Row = 0 + UseOwnFont = False + Visible = True + WholeRow = True + end + object lbLoadDatabase: TdxBarLargeButton + Caption = 'Load Database' + Category = 0 + Hint = 'Load Database' + Visible = ivAlways + LargeImageIndex = 0 + OnClick = lbLoadDatabaseClick + SyncImageIndex = False + ImageIndex = 0 + end + object lbSaveDatabase: TdxBarLargeButton + Caption = 'Save Database' + Category = 0 + Enabled = False + Hint = 'Save Database' + Visible = ivAlways + LargeImageIndex = 1 + ShortCut = 16467 + OnClick = lbSaveDatabaseClick + end + object lbCreateDefinition: TdxBarLargeButton + Caption = 'Create Definition' + Category = 0 + Hint = 'Create Definition' + Visible = ivAlways + LargeImageIndex = 2 + OnClick = lbCreateDefinitionClick + end + object piStatusBarProgress: TdxBarProgressItem + Align = iaClient + Caption = 'Working' + Category = 0 + Hint = 'Working' + Visible = ivInCustomizing + Width = 300 + Position = 30 + Smooth = True + end + object bbDuplicateDefinition: TdxBarButton + Align = iaClient + Caption = 'Duplicate Definition' + Category = 0 + Enabled = False + Hint = 'Duplicate Definition' + Visible = ivAlways + ImageIndex = 4 + OnClick = bbDuplicateDefinitionClick + end + object bbDeleteDefinition: TdxBarButton + Align = iaClient + Caption = 'Delete Definition' + Category = 0 + Enabled = False + Hint = 'Delete Definition' + Visible = ivAlways + ImageIndex = 5 + OnClick = bbDeleteDefinitionClick + end + object lbGenerate: TdxBarLargeButton + Caption = 'Generate C++ Files' + Category = 0 + Hint = 'Generate C++ Files' + Visible = ivAlways + LargeImageIndex = 5 + OnClick = lbGenerateClick + AutoGrayScale = False + SyncImageIndex = False + ImageIndex = 3 + end + object dxBarSeparator1: TdxBarSeparator + Caption = 'Editor' + Category = 0 + Hint = 'Editor' + Visible = ivAlways + end + object dxBarSeparator2: TdxBarSeparator + Category = 0 + Visible = ivAlways + ShowCaption = False + end + object dxBarSeparator3: TdxBarSeparator + Caption = 'Clipboard' + Category = 0 + Hint = 'Clipboard' + Visible = ivAlways + end + object bbClipboardCopy: TdxBarButton + Align = iaClient + Caption = 'Copy Definition' + Category = 0 + Enabled = False + Hint = 'Copy Definition' + Visible = ivAlways + ImageIndex = 8 + OnClick = bbClipboardCopyClick + end + object lbClipboardPaste: TdxBarLargeButton + Caption = 'Paste Definition' + Category = 0 + Hint = 'Paste Definition' + Visible = ivAlways + LargeImageIndex = 6 + OnClick = lbClipboardPasteClick + SyncImageIndex = False + ImageIndex = 9 + end + object bbClipboardCut: TdxBarButton + Align = iaClient + Caption = 'Cut Definition' + Category = 0 + Enabled = False + Hint = 'Cut Definition' + Visible = ivAlways + ImageIndex = 10 + OnClick = bbClipboardCutClick + end + object lbMnemonicFilter: TdxBarLargeButton + Caption = 'Mnemonic Filter' + Category = 0 + Hint = 'Mnemonic Filter' + Visible = ivAlways + AllowAllUp = True + ButtonStyle = bsChecked + LargeImageIndex = 7 + OnClick = lbMnemonicFilterClick + end + object bbExpandNodes: TdxBarButton + Align = iaClient + Caption = 'Expand all nodes' + Category = 0 + Hint = 'Expand all nodes' + Visible = ivAlways + ImageIndex = 6 + OnClick = bbExpandNodesClick + end + object bbCollapseNodes: TdxBarButton + Align = iaClient + Caption = 'Collapse all nodes' + Category = 0 + Hint = 'Collapse all nodes' + Visible = ivAlways + ImageIndex = 7 + OnClick = bbCollapseNodesClick + end + object edtMnemonicFilter: TdxBarEdit + Caption = 'Mnemonic Filter' + Category = 0 + Hint = 'Mnemonic Filter' + Visible = ivAlways + OnCurChange = edtMnemonicFilterCurChange + ShowCaption = True + end + object bbExactMatch: TdxBarButton + Caption = 'Exact Match' + Category = 0 + Hint = 'Exact Match' + Visible = ivAlways + AllowAllUp = True + ButtonStyle = bsChecked + ImageIndex = 11 + OnClick = bbExactMatchClick + end + object dxBarSeparator4: TdxBarSeparator + Caption = 'New Separator' + Category = 0 + Hint = 'New Separator' + Visible = ivAlways + ShowCaption = False + end + object bbExpandLeaf: TdxBarButton + Caption = 'Expand' + Category = 0 + Hint = 'Expand' + Visible = ivAlways + ImageIndex = 6 + OnClick = bbExpandLeafClick + end + object bbCollapseLeaf: TdxBarButton + Caption = 'Collapse' + Category = 0 + Hint = 'Collapse' + Visible = ivAlways + ImageIndex = 7 + OnClick = bbCollapseLeafClick + end + end + object SkinController: TdxSkinController + SkinName = 'Seven' + Left = 552 + Top = 240 + end + object imgIcons16: TcxImageList + FormatVersion = 1 + DesignInfo = 11534744 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000220000 + 0033000000330000003300000033000000330000002200000000000000000000 + 000000000000000000000000000000000000000000000000002A77736BBCB0AA + 9EFFAFA99DFFAEA89CFFAEA99CFFB0AA9EFF77736BBC0000002B000000000000 + 000B0000000A0000000A0000000A0000000A000000079B8F81DBBAB3A6FFD3CA + C1FFE4DAD2FFEFE4DDFFE8DDD6FFD5CDC4FFB9B4A7FF959085DD0000002F0F21 + 2D5F0E1F2A5C0E1F2A5C0E1F2A5C0D1D285908161F51BCAD9AFFCBC3B8FFD4CC + C4FFE1D6CEFFEBE1D9FFE4DAD2FFD7D0C7FFCDC6BCFFB1AB9FFF4190C5F14398 + D2FF4094D0FF3E92CFFF3E92CEFF3D92D0FF3592D5FFBAAA98FFCAC2B7FFD3CB + C2FFDFD5CDFFEAE0D8FFE3D8D0FFD6CEC5FFCCC5BBFFB0AA9EFF4499D2FF3F94 + D0FFABFBFFFF9BF3FFFF92F1FFFF92F1FFFF8CF4FFFFB6A695FFC8BFB4FFD1C8 + C0FFDED3CBFFE8DED6FFE1D6CEFFD4CCC3FFCAC3B9FFB0AA9EFF4397D1FF56AC + DDFF8EDAF5FFA2EDFFFF82E5FEFF83E6FFFF7EEAFFFFB4A493FFE5DDD5FFF8F4 + EEFFFDF9F2FFF9F5EDFFFDF8F2FFF9F4EEFFE6DFD8FFAEA99CFF4296D1FF71C4 + EAFF6CBCE6FFBBF2FFFF75DEFDFF75DFFEFF73E3FFFFB4A392FFF0E5DCFFDDD3 + C8FFD8CFC5FFD8CFC5FFD8CFC5FFDDD3C8FFEFE7DEFFB0A99CFF4095D0FF90DD + F8FF44A0D8FFDDFCFFFFDAFAFFFFDAFAFFFFD9FDFFFFA5B3AEFFD1C2B4FFEBE0 + D5FFEDE4DAFFEDE4DAFFEDE4DAFFEAE0D5FFD0C4B6FF928779D13E93CFFFB2F6 + FFFF51ACDEFF358ACAFF358ACAFF358ACBFF348BCDFF5ABDEAFF94BDC4FFAEA6 + 99FFAFA393FFAEA393FFAEA293FFB1A89AFFAFC0C2FF2C6A94C43D92CFFFB8F3 + FFFF77DFFEFF7BE0FEFF7CE1FEFF7CE1FFFF7DE2FFFF50ACE0FF51BBEDFFD3FB + FFFFCFFAFFFFCEF9FFFFCEF9FFFFD0FAFFFFD7FEFFFF3B95D3FF3C92CFFFC0F3 + FFFF70D9FBFF73DAFBFF74DAFBFF74DAFBFF74DBFCFF76DEFDFF4FAADDFF358C + CBFF338CCCFF328CCCFF328DCDFF3690CFFF3C94D0FF2D688FAE3B92CFFFCAF6 + FFFF69D5F9FF6CD5F9FF6AD4F9FF69D4F9FF69D5F9FF6AD6FAFF6BD8FBFF6BD9 + FCFF6BDAFDFF69DAFDFFDAFDFFFF3C93D0FF0A171F2F000000003B92CFFFD5F7 + FFFF60D1F9FF61D0F8FFB4EBFDFFD9F6FFFFDAF8FFFFDAF8FFFFDAF9FFFFDBF9 + FFFFDAF9FFFFDAFAFFFFDFFEFFFF3D94D0FF0E202C35000000003D94D0FFDCFC + FFFFD8F7FFFFD8F7FFFFDBFAFFFF358ECDFF3991CEFF3A92CFFF3A92CFFF3A92 + CFFF3A92CFFF3A92CFFF3D94D0FF3D8BC1EA00000000000000003F8EC5EF3D94 + D0FF3A92CFFF3A92CFFF3D94D0FF3A87BBE40B1A232B0D1E29320E1E2A330E1E + 2A330E1E2A330E1F2A330F212E37000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000190000 + 0033000000330000003300000033000000330000000800000008000000330000 + 003300000033000000330000003300000033000000330000001959400C96B782 + 18FFB68014FFB37A06FFD7CFD2FFD8CEC9FF232221531D130053D9CFCEFFD5CA + C3FFD4CAC4FFD6CFD2FFB37A06FFB68014FFB78218FF59400C96B78218FFF6CD + 8BFFF2C67DFFF0C171FFFAF7FBFFFFFFFFFF4C4848FF989392FFFFFFFFFFF7EF + EAFFF6EFEBFFF9F6FAFFF0C171FFF2C67DFFF6CD8BFFB78218FFB68116FFF3CA + 87FFEDBC6DFFEBB761FFF8F5F7FFFFFFFFFF4A4541FF948C88FFFFFFFFFFF1E8 + E0FFF0E7E0FFF7F4F7FFEBB761FFEDBC6DFFF3CA87FFB68116FFB68116FFF1CB + 89FFE9B762FFE7B257FFF9F8FBFFFDF7F2FF877F79FF4A4441FFFEF7F2FFEEE3 + D8FFEDE2D9FFF8F7FBFFE8B257FFE9B762FFF1CB89FFB68116FFB68116FFF3CC + 8EFFE8B25AFFE7AE51FFFCFFFFFFECE0D7FFF1E4DAFFF1E5DAFFEDE0D5FFEADD + D3FFE9DED5FFFBFFFFFFE7AE51FFE8B25AFFF3CC8EFFB68116FFB68115FFF3CE + 94FFE6AE51FFE5AB4BFFE6C9A4FFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFDFF + FFFFFEFFFFFFE6C9A4FFE5AC4BFFE6AE51FFF3CE94FFB68115FFB68115FFF3D0 + 9AFFE5A845FFE3A640FFE2A136FFE29E2FFFE19D2DFFE19D2CFFE19D2CFFE19D + 2DFFE29E2FFFE2A136FFE3A640FFE5A845FFF3D09AFFB68115FFB68114FFF4D4 + A0FFE1A136FFF2DEB7FFFCFFFFFFFBFFFDFFFBFFFCFFFBFFFDFFFBFFFDFFFBFF + FDFFFBFFFDFFFBFFFFFFF2DEB7FFE1A136FFF4D4A0FFB68114FFB68014FFF6D8 + A7FFE09C27FFFBFFFFFFFCFBF3FFFCF9EFFFFBF8EEFFFCFAF0FFFCFAF0FFFBF9 + EEFFF9F8EDFFFAF9F1FFFAFEFEFFE09B27FFF6D8A7FFB68014FFB68014FFF8DC + B0FFE0981CFFFBFBF8FF79787BFFA2A0A2FFFCF6EAFF797879FFA3A1A3FFA09F + A1FFFAF4E9FF9D9DA0FFF9F9F6FFE0981CFFF8DCB0FFB68014FFB68113FFFCE3 + BCFF9B6104FFFDFCF9FFFDF5E8FFFEF4E7FFFBF2E5FFFCF2E5FFFBF2E5FFFBF2 + E5FFFAF1E3FFF9F1E5FFFCFAF7FF9A6104FFFCE3BCFFB68113FFB68012FFFEE9 + C6FF714100FFFFFFFFFF79797AFF7A7A7AFFA2A1A1FF9F9F9FFFF6ECDEFF7777 + 77FFA1A1A1FF9E9FA0FFFFFFFFFF704100FFFEE9C6FFB68012FFB68012FFFDEC + D1FFDA8600FFFFFFFFFFF1E5D8FFF2E5D8FFF2E5D7FFF0E3D6FFEFE2D5FFF1E4 + D7FFF1E4D6FFEFE3D6FFFFFFFFFFDA8600FFFDECD1FFB68012FFB78115FFFFEC + CDFFFCE7C3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE7C3FFFFECCDFFB78115FF593F0C7CB781 + 14FFB57E0FFFB57C0BFFB57C09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C + 09FFB57C09FFB57C09FFB57C0BFFB57E0FFFB78114FF593F0C7C} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000002200000033000000330000003300000033000000330000 + 0033000000330000002300000000000000000000000000000000000000000000 + 000A000000258C6839BCCE9956FFCE964EFFAFB4BAFFB1B1B2FFB1B1B1FFB1B1 + B1FFB2B2B2FF7C7C7CBF000000000000000000000000000000000000001F2323 + 205C7E7C77C5D09851FFEDDEBAFFE6CF9FFFFAFCFFFFFAF9FAFFFAF9F9FFFAF9 + F9FFFFFFFFFFB2B2B2FF000000330000002300000000000000006B6760ADB4AF + A4FFC4C1BBFFCE964EFFEBD9B5FFE1C38CFFF0F3F7FFEFF0F0FFEFEFEEFFF0F0 + EFFFF9F9F8FFB0B0B0FFB3B3B3FF7C7C7CBE0000000000000000B1AB9FFFC8C2 + B7FFD0CBC8FFCD954DFFECDAB7FFDEBB7EFFEBECF1FFEAE9E9FFEAE9E8FFEAE9 + E8FFF6F6F5FFADADADFFFFFFFFFFB3B3B3FF0000003300000023B0AB9EFFC8C1 + B8FFCEC9C6FFCC944BFFEFDCBBFFDCB36FFFE5E7EBFFE4E4E3FFE4E3E2FFE5E3 + E2FFF5F5F4FFACACACFFFEFEFCFFB0B1B1FFB3B3B3FF7C7C7CBFB0AA9EFFCAC3 + BAFFECEAE7FFCC944AFFF2E2C3FFF2DEBBFFF6F8FCFFF6F5F5FFF5F4F4FFF5F4 + F4FFF7F6F6FFACACADFFFAFAF9FFAEAEAEFFFFFFFFFFB3B3B3FF6F6B64ADF9F4 + EEFFD8D4CFFFC59F6EFFD09749FFD59843FFB5B7B2FFB3B3ADFFAFAFACFFADAD + ACFFACADADFFC3C3C2FFF8F8F7FFADADADFFFEFEFDFFB2B2B2FF706C64B4A39A + 8FFFBAB3AAFFCCC5BEFFE7E0D3FF1F39DCFF9DAFFFFF98A9FFFFFFFEF7FFFCF9 + F8FFF9F7F7FFF8F7F7FFF8F7F7FFACACADFFFBFBFAFFB2B2B2FFB1AB9FFFC9C2 + B9FFCFC8BFFFD7CEC6FFE5DBCBFF7179D3FF293BD5FF2D39D8FFC3B6B1FFB9B0 + B1FFB1AEAEFFADADADFFADADADFFC3C3C2FFF9F9F8FFB2B2B2FFB0AB9EFFC7C0 + B6FFCCC4BBFFD3CAC1FFDDD3C7FFEDDFD0FFFFF0E0FF009B5AFF79EBD3FF6EE6 + D0FFFFF9FCFFFBF8F9FFF9F8F8FFF9F7F8FFFAF9F9FFB3B3B3FFAFAA9DFFC8C1 + B7FFE8E3DBFFF5F0E8FFFDF9F2FFFFF9F2FFFFFDF6FF4CBC98FF00955FFF0094 + 5CFFB9AFB3FFB0AFB1FFB1B1B2FFB2B2B2FFB3B3B3FF7D7D7DB0AEA89CFFFBF5 + EFFFEEE7DEFFE0D8CEFFDDD5CCFFDDD5CCFFE2D6CEFFEBD9D2FFF1DBD5FFF1DD + D6FFF6E9E1FFFDF7EEFFAEA799FF000000000000000000000000AFA99DFFE1D8 + CEFFE1D7CEFFDFD6CCFFDFD5CBFFDFD5CBFFDFD6CCFFE1D6CCFFE2D6CDFFE2D7 + CDFFE2D8CEFFE2D8CEFFAFA99CFF0000000000000000000000006A675F99B6B0 + A4FFD6CFC4FFE6DED5FFEFE7DDFFEEE6DCFFEEE6DCFFEEE6DCFFEFE7DDFFE6DE + D5FFD6CFC4FFB6B0A4FF6A675F99000000000000000000000000000000002322 + 2033827D74BCAEA99CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA9 + 9CFF827D74BC2322203300000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000002200000033000000330000 + 0033000000330000003300000033000000330000003300000030000000000000 + 00000000000000000023000000330000003372665BB9AB9787FFAB9584FFAA95 + 84FFA99584FFA99585FFA99585FFA99685FFAA9787FFA39183F2000000000000 + 000000000000786A60BFAB9887FFAA9787FFA99584FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA9787FF000000300000 + 003300000033AA9686FFFFFFFFFFFFFFFFFFA58F7EFFFFFFFFFF00A4FFFF00A4 + FFFFFFFCFAFFBFA282FFBFA385FFBDA282FFFFFFFFFFA89585FFA39183F2AA98 + 88FFAA9686FFA69282FFFFFFFFFFC0A485FFA38E7EFFFFFFFFFFF8F3F3FFF9F4 + F6FFF3F4F9FFF1F5FDFFF0F5FEFFEFF3F9FFFFFFFFFFA89584FFAA9787FFFFFF + FFFFFFFFFFFFA39080FFFFFFFFFFFEFFFFFFA28F7FFFFFFFFFFFCE8C2DFFCF8E + 33FFCE8F34FFCE8F35FFCD8F34FFCC8C2EFFFFFFFFFFA89585FFA89586FFFFFF + FFFFCE8F32FFA39185FFFFFFFFFFE5A84CFFA39183FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA9787FFA89586FFFFFF + FFFFCE9138FFA49388FFFFFFFFFFC58223FFC3B8B2FFA58F7FFFA58E7EFFA58E + 7EFFA58E7EFFA48E7EFFA48F7FFFA79382FFAA9787FFA29082EDA89586FFFFFF + FFFFCB8E35FFA59489FFFFFFFFFFF2EEF4FFF7F0F5FFF9F1F5FFF9F1F5FFF9F1 + F5FFF8F0F4FFF5EFF2FFFFFFFFFFAA9686FF0000000000000000A89686FFFFFF + FFFFC6892FFFA69489FFFFFFFFFF30A977FF37AB7AFF38AC7BFF38AC7BFF38AC + 7BFF37AB7AFF31A977FFFFFFFFFFAA9585FF0000000000000000A99686FFFFFF + FFFFC3832AFFA7978EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFAB9787FF0000000000000000A99686FFFFFF + FFFFBC8027FFB49571FFA89991FFA7968CFFA8968CFFA8958BFFA7958BFFA795 + 8AFFA69386FFA59081FFA99584FF72665CA80000000000000000A99686FFFFFF + FFFFBA7D26FFBF8532FFC58D3AFFCA9342FFCE9849FFCF9A4FFFD09D53FFCE9D + 53FFCA964CFFFFFFFFFFAA9687FF000000000000000000000000A99686FFFFFF + FFFFB7781DFFBA7D27FFBC7F2BFFBE8431FFC18838FFC28C3EFFC38E42FFC491 + 45FFC58F43FFFFFFFFFFA99686FF000000000000000000000000AA9787FFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFAA9787FF000000000000000000000000A39183EFAA97 + 87FFA99686FFA99686FFA99686FFA99686FFA99686FFA89686FFA89686FFA895 + 86FFA99586FFAA9787FFA39183EF000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000330000003300000000000000000000000000000000000000000000 + 000A000000260000003300000033000000330000003300000033000000330000 + 00336C6A69FF6C6A69FF0000000E0000003300000033000000000000001F2322 + 205C837E75C9B0AA9EFFAFA99DFFAEA89CFFC9C3B6FF73716FFF777573FFCBC4 + B8FF999796FF999796FF1C1C1C69777573FF73716FFF000000006B6760ADB4AE + A2FFC5BEB3FFD4CAC2FFE1D7CFFFE8DED6FFFBEFE8FF7F7D7BFFC7C4C3FF8684 + 83FFBCBAB9FFBCBAB9FF868483FFC7C4C3FF787675FF00000000B1AB9FFFC8C1 + B6FFCFC7BFFFD6CCC5FFDCD3CAFFE9DDD6FFFFFFFFFFC2BEB8FF878584FFA9A7 + A6FF817F7DFF817F7DFFA9A7A6FF878584FF2E2D2C8900000033B0AB9EFFC7C0 + B6FFCDC4BBFFD3CAC2FFDAD1C8FFFFFFFCFF716F6DFFA19F9EFFC1BFBDFF8380 + 7FFFFCF2E6FFF5EAE0FF83807FFFC1BFBDFFA09E9DFF7B7977FFB0AA9EFFCAC3 + B9FFEAE5DDFFF7F3EBFFFFFBF4FFFFFFFBFF72706EFFA3A09FFFC4C2C1FF8684 + 80FFEFEEEEFFD1CAC1FF868480FFC3C1C1FFA19F9EFF7C7A78FF6F6B64ADF9F3 + EDFFD8D1C9FFC2B8B0FFBFB6AEFFC5BAB2FFE1D5CBFFA8A09BFF8C8A88FFB2AF + AEFF878584FF878584FFB2AFAEFF8C8A88FF3837369B00000000706C64B4A39A + 8FFFBAB2A7FFCAC1B8FFDED5CCFFE5DBD3FFFFFFFFFF868481FFD5D3D2FF8D8B + 89FFC9C7C6FFC9C7C6FF8D8B89FFD5D3D2FF7F7D7BFF00000000B1AB9FFFC9C2 + B9FFCFC7BFFFD5CCC4FFDCD3CAFFE3D9D1FFF7EBE4FF7A7876FF878583FFD9D9 + D8FFA9A7A5FFA9A7A5FFC0BBAFFF878583FF7B7977FF00000000B0AB9EFFC7C0 + B6FFCCC4BBFFD3C9C1FFDAD0C7FFE1D7CFFFEBE1DAFFF4E8DFFFFFFBF2FFF3E9 + DDFF767472FF767472FFBDB6AAFF000000000000000000000000AFAA9DFFC8C1 + B7FFE8E3DBFFF5F0E8FFFDF8F2FFFCF8F1FFFBF7F0FFFCF7F1FFFCF8F1FFF6F0 + EAFFE9E2DBFFCAC2BAFFAFA99DFF000000000000000000000000AEA89CFFFBF5 + EFFFEEE7DEFFE0D8CEFFDDD5CCFFDDD4CBFFDDD4CBFFDDD4CBFFDDD5CCFFE0D7 + CEFFEDE6DEFFFBF6EFFFAEA89CFF000000000000000000000000AFA99DFFE1D8 + CEFFE1D7CEFFDFD6CCFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5CBFFDFD6 + CCFFE1D7CEFFE1D8CEFFAFA99DFF0000000000000000000000006A675F99B6B0 + A4FFD6CFC4FFE6DED5FFEFE7DDFFEEE6DCFFEEE6DCFFEEE6DCFFEFE7DDFFE6DE + D5FFD6CFC4FFB6B0A4FF6A675F99000000000000000000000000000000002322 + 2033827D74BCAEA99CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA9 + 9CFF827D74BC2322203300000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000001E0000003300000033000000330000001E00000000000000000000 + 000A000000260000003300000033000000330000003300000033000000330000 + 00331C2A79A9374DCCFF384DCBFF384DCCFF212F79AC0000001E0000001F2322 + 205C837E75C9B0AA9EFFAFA99DFFAEA89CFFAEA89CFFB0AA9CFFB9B199FF6C76 + B8FF324ED9FF375CF9FF375DFAFF385DF9FF3852D7FF202E79AC6B6760ADB4AE + A2FFC5BEB3FFD4CAC2FFE1D7CFFFE8DED6FFEFE4DDFFEDE2D8FFF0E3CEFF213C + CEFF3E62FCFF3B60FAFF3A5DF8FF3C60FAFF4165FBFF344BCCFFB1AB9FFFC8C1 + B6FFCFC7BFFFD6CCC5FFDCD3CAFFE4D9D2FFEBE0D9FFE8DED4FFEEE0CAFF1F39 + CBFFA6B8FFFFFFFFFFFFFFFFFFFFFFFFFFFFA9BAFFFF3148CAFFB0AB9EFFC7C0 + B6FFCDC4BBFFD3CAC2FFDAD1C8FFE2D7CFFFECE1DAFFE7DBD1FFEBDDC9FF1C37 + CAFF5875FEFF5775FEFF5473FDFF5776FEFF5D79FFFF334ACBFFB0AA9EFFCAC3 + B9FFEAE5DDFFF7F3EBFFFFFBF4FFFDFAF3FFFDF9F2FFFFFAF3FFFFFFF3FF8993 + DDFF3954DEFF6C86FFFF728AFFFF6F89FFFF465EDDFF1F2D79976F6B64ADF9F3 + EDFFD8D1C9FFC2B8B0FFBFB6AEFFC5BAB2FFC8BEB5FFC6BCB3FFC4BAAFFFD1C5 + B2FF6771BAFF233ED0FF2942CEFF2F47CDFF1F2D799700000000706C64B4A39A + 8FFFBAB2A7FFCAC1B8FFDED5CCFFE5DBD3FFECE1DAFFE8DED5FFE1D7CEFFD1C6 + BBFFC6BBAAFFB4A790FF746D5AAB000000000000000000000000B1AB9FFFC9C2 + B9FFCFC7BFFFD5CCC4FFDCD3CAFFE3D9D1FFEADFD8FFE5DCD3FFDED4CCFFD8CF + C5FFD3CBC1FFCEC6BAFFB7B09DFF000000000000000000000000B0AB9EFFC7C0 + B6FFCCC4BBFFD3C9C1FFDAD0C7FFE1D7CFFFEBE1DAFFE4D9D1FFDCD1C9FFD5CC + C2FFCEC6BDFFCAC1B9FFB1AB9EFF000000000000000000000000AFAA9DFFC8C1 + B7FFE8E3DBFFF5F0E8FFFDF8F2FFFCF8F1FFFBF7F0FFFCF7F1FFFCF8F1FFF6F0 + EAFFE9E2DBFFCAC2BAFFAFA99DFF000000000000000000000000AEA89CFFFBF5 + EFFFEEE7DEFFE0D8CEFFDDD5CCFFDDD4CBFFDDD4CBFFDDD4CBFFDDD5CCFFE0D7 + CEFFEDE6DEFFFBF6EFFFAEA89CFF000000000000000000000000AFA99DFFE1D8 + CEFFE1D7CEFFDFD6CCFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5CBFFDFD6 + CCFFE1D7CEFFE1D8CEFFAFA99DFF0000000000000000000000006A675F99B6B0 + A4FFD6CFC4FFE6DED5FFEFE7DDFFEEE6DCFFEEE6DCFFEEE6DCFFEFE7DDFFE6DE + D5FFD6CFC4FFB6B0A4FF6A675F99000000000000000000000000000000002322 + 2033827D74BCAEA99CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA9 + 9CFF827D74BC2322203300000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000300000 + 0033000000330000003300000033000000330000003300000033000000330000 + 003300000033000000330000003300000033000000330000003000652DF20067 + 2EFF00662EFF00672DFF006D25FF0A32A7FF0B369CFF0C3699FF0C3699FF0B36 + 9CFF0A32A7FF006D25FF00672DFF00662EFF00672EFF00652DF200682EFF98E9 + AAFF26BF4CFF29C14CFF2DC845FF0933A8FF187AE3FF0A6FDBFF0A6FDBFF187A + E3FF0933A8FF2DC845FF29C14CFF26BF4CFF98E9AAFF00682EFF00672DFFA7D2 + 99FFA2CE96FFA4CF95FFADD58CFF0233A9FF3187E3FF0C6ED7FF0C6ED7FF3187 + E3FF0233A9FFADD58CFFA4CF95FFA2CE96FFA7D299FF00672DFF995105FFAD45 + 00FFA84300FFAA4400FFB64300FF1582F0FF2CAEFFFF30AEFFFF30AEFFFF2CAE + FFFF1582F0FFB64300FFAA4400FFA84300FFAD4500FF995005FFAC4F00FFFFC4 + B1FFF87D52FFFA7D51FFFF7A44FF30BDFFFF3DBAFFFF0E71DAFF0E71DAFF3DBA + FFFF30BDFFFFFF7A44FFFA7D51FFF87D53FFFFC4B2FFAB4E00FFA94F00FFD1AB + B9FFCFA9B2FFD0A7B0FFD7A1A6FF46CEFFFF4AC8FFFF1688E9FF1688E9FF4AC8 + FFFF46CEFFFFD7A1A6FFD0A7B0FFCFA9B2FFD0ABBAFFB35400FF261580FF0000 + 9AFF000093FF000091FF00008CFF35A6ECFFECFFFFFFE8FFFFFFE8FFFFFFECFF + FFFF35A6ECFF00008CFF000091FF000093FF00009BFF402166FF090699FFABCD + FFFF4A81FFFF4A80FFFF4479FFFFD2E3F9FFA6957BFFB9772BFFB9772BFFA695 + 7BFFD2E3F9FF4479FFFF4A80FFFF4A81FFFFAACDFFFF07059AFF0C0796FFCCE9 + FFFFC6E0FFFFC4DEFFFFBCD7FFFFCCE6FFFFB97A2EFFF8C87EFFF8C87EFFB97A + 2EFFCCE6FFFFBCD7FFFFC4DEFFFFC6E0FFFFCCE9FFFF0B0796FF110D8FEF0B06 + 95FF070193FF000094FF000097FFCDEEFFFFB77D35FFF0C582FFEDBB6EFFB77D + 36FFCDEEFFFF000097FF000094FF070193FF0B0695FF0C0A6AB0000000000000 + 00000000000000000000795520ACBD8338FFB57E3BFFEAC387FFE3B064FFB57E + 3CFFBD8338FF795520AC00000000000000000000000000000000000000000000 + 00000000000000000000C18A3FFFF2D093FFDCA75AFFD9A65AFFDAA65AFFDCA7 + 5AFFF2D093FFC18A3FFF00000000000000000000000000000000000000000000 + 0000000000000000000000000000B78141FFE5C791FED19C4DFFD19C4DFFE5C7 + 91FEB78141FF0000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000B78141FFCDB389EACDB389EAB781 + 41FF000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B88342FFB88342FF0000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000300000 + 0033000000330000003300000033000000330000003300000033000000330000 + 003300000033000000330000003300000033000000330000003000652DF20067 + 2EFF00662EFF00672DFF006D25FF0A32A7FF0B369CFF0C3699FF0C3699FF0B36 + 9CFF0A32A7FF006D25FF00672DFF00662EFF00672EFF00652DF200682EFF98E9 + AAFF26BF4CFF29C14CFF2DC845FF0933A8FF187AE3FF0A6FDBFF0A6FDBFF187A + E3FF0933A8FF2DC845FF29C14CFF26BF4CFF98E9AAFF00682EFF00672DFFA7D2 + 99FFA2CE96FFA4CF95FFADD58CFF0233A9FF3187E3FF0C6ED7FF0C6ED7FF3187 + E3FF0233A9FFADD58CFFA4CF95FFA2CE96FFA7D299FF00672DFF995105FFAD45 + 00FFA84300FFAA4400FFB64300FF1582F0FF2CAEFFFF30AEFFFF30AEFFFF2CAE + FFFF1582F0FFB64300FFAA4400FFA84300FFAD4500FF995005FFAC4F00FFFFC4 + B1FFF87D52FFFA7D51FFFF7A44FF31BDFFFF3EBAFFFF0E72DAFF0E72DAFF3EBA + FFFF31BDFFFFFF7A44FFFA7D51FFF87D53FFFFC4B2FFAB4E00FFA94F00FFD1AB + B9FFCFA9B2FFD0A8B0FFD8A2A6FF49D0FFFF4FC9FFFF1B89E8FF1B89E8FF4FC9 + FFFF49D0FFFFD8A2A6FFD0A8B0FFCFA9B2FFD0ABBAFFB35400FF261580FF0000 + 9AFF000093FF000091FF00008DFF40AAEBFF5FDAFFFFF7FFFFFFF7FFFFFF5FDA + FFFF40AAEBFF00008DFF000091FF000093FF00009BFF402166FF090699FFABCD + FFFF4A81FFFF4B80FFFF4B7CFFFF084BBCFFFFFFFFFF1B9654FF1B9654FFFFFF + FFFF084BBCFF4B7CFFFF4B80FFFF4A81FFFFAACDFFFF07059AFF0C0796FFCCE9 + FFFFC6E0FFFFC3DBFFFFC1D3FFFFE7E4FFFF007E2DFF2AEECAFF2AEECAFF007E + 2DFFE7E4FFFFC1D3FFFFC3DBFFFFC6E0FFFFCCE9FFFF0B0796FF110D8FEF0B06 + 95FF070092FF020092FFE5CDF6FF008230FF11E3BFFF00D498FF00D499FF12E3 + BFFF008230FFE5CDF6FF020092FF070092FF0B0695FF0C0A6AB0000000000000 + 0000000000000000000000923CFF1BEAC8FF00CDA0FF00C99CFF00C99CFF00CD + A0FF1BEAC8FF00923CFF00000000000000000000000000000000000000000000 + 00000000000000000000006431AE008A47FF00843FFF4CD7C0FF00C39FFF0085 + 41FF008A47FF006431AE00000000000000000000000000000000000000000000 + 000000000000000000000000000000000000008843FF6FDED1FF00BEA1FF0089 + 45FF000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000008A46FF86E4DEFF88E5DFFF008A + 47FF000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000006134B0008A47FF008A47FF0061 + 34B0000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0023000000330000003300000033000000330000003300000033000000330000 + 0033000000330000003300000033000000230000000000000000000000002B48 + 66C0396592FF356291FF346191FF346191FF346192FF346293FF346293FF3462 + 93FF346293FF356292FF396593FF2B4866C00000000000000000000000003967 + 94FF96A8B1FFBEB9B1FFBAB6AFFFBAB6AFFFBBB7B3FFBCBBBAFFBDBCBEFFBDBC + BEFFBDBBBAFFC0BBB5FF97A8B2FF396794FF0000000000000000000000003665 + 95FFBEB9B1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBFBAB2FF366595FF0000000000000000000000003665 + 95FFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBBB6AFFF366595FF0000000000000000000000003668 + 96FFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBAB5AEFF366896FF0000000000000000000000003769 + 97FFBAB5AEFFFFFFFFFFFFFFFFFFFCFBFBFFFCFBFBFFFFFFFFFFFCFBFBFFFCFB + FBFFFFFFFFFFFFFFFFFFBAB5AEFF376997FF000000000000000000000000386B + 9AFFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBAB5AEFF386B9AFF000000000000000000000000396C + 9BFFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBAB5AEFF396C9BFF000000000000000000000000396D + 9CFFBAB5AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBAB5AFFF396D9CFF0000000000000000000000003A6F + 9EFFB9B5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFB9B5AEFF3A6F9EFF0000000000000000000000003B70 + 9FFFBAB4ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFBAB4ADFF3B709FFF0000000000000000000000003C73 + A0FFBCB6AEFFFFFFFFFFFFFFFFFF999592FF9B9895FF9B9895FF9B9895FF9995 + 92FFFFFFFFFFFFFFFFFFBCB6AEFF3C73A0FF0000000000000000000000004076 + A2FFC5CACFFFBCB7AFFFBBB7B1FF908B88FFCAC8C7FFC9C7C6FFCAC8C7FF908B + 88FFBBB7B1FFBCB7AFFFC5CACFFF4076A2FF0000000000000000000000003154 + 71B04278A4FF3E77A4FF3A76A7FF867F7AFFB8B5B5FFB5B3B3FFB8B5B5FF867F + 7AFF3A76A7FF3E77A4FF4278A4FF315471B00000000000000000000000000000 + 000000000000000000000000000000000000979796FF959595FF979796FF0000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000002E00000033000000330000 + 0033000000330000003300000033000000330000003300000030000000230000 + 003300000033000000330000003300000033ADA9A3ECB1B0AEFFAEAEABFFADAD + ABFFADADABFFADADABFFADADABFFAEAEABFFAFAFADFFA8A8A6F22C4866C04068 + 93FF3F6893FF406893FF3E6793FF335F8FFFBBB7B0FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0B0ADFF3F6A94FF568A + B0FF4C82ABFF4D82ABFF4B81ABFF417BA8FFB9B4ADFFFFFFFFFFFDFDFDFFFFFF + FFFFFFFFFFFFFFFFFFFFFEFEFEFFFCFCFCFFFFFFFFFFAEAEABFF3F6A95FF6594 + BBFF4C83AEFF4E83AEFF4C82AEFF427DACFFB8B3ACFFFFFFFFFFFBFBFAFF8181 + 81FFB5B5B6FFB4B4B4FFB1B1B1FFF9F9F8FFFFFFFFFFADADABFF3F6B95FF74A1 + C3FF4F86B1FF5187B2FF4F86B2FF4581B0FFB8B2ABFFFFFFFFFFF6F6F5FFFBFB + FAFFFBFBFAFFF9F9F8FFF7F7F6FFF4F4F3FFFFFFFFFFADADABFF3F6B97FF82AB + CCFF5088B5FF528AB6FF5189B6FF4784B5FFB8B2ABFFFFFFFFFFF3F2F2FF8484 + 84FFB2B2B3FFF5F4F4FFAEAEAEFFF1F0F0FFFFFFFFFFADADABFF3E6C97FF91B6 + D5FF528BBAFF558CBBFF548CBBFF4B88BBFFB8B2ABFFFFFFFFFFEEEDEDFFF3F2 + F2FFF3F2F2FFF1F0F0FFF0EFEFFFEDEBEBFFFFFFFFFFADADABFF3E6D97FF9FC1 + DDFF538EBDFF5791BFFF5691BFFF4D8CBFFFB7B1AAFFFFFFFFFFE9E9E8FF8585 + 85FFB2B1B1FFB0AFAFFFAEADADFFE8E8E6FFFFFFFFFFAEAEABFF3E6E99FFACCA + E4FF5691C1FF5A93C3FF5993C3FF508FC3FFB8B2AAFFFFFFFFFFE3E2E0FFE6E5 + E4FFE7E5E4FFE6E5E4FFE4E3E2FFE2E1E0FFFFFFFFFFAEAEACFF3E6F9AFFB9D5 + EAFF5794C7FF5B97C8FF5B98C9FF5395CAFFBAB3ABFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAFAFADFF3D709AFFC6DE + F0FF5797CCFF5A9ACFFF589AD1FF5398D2FF92AABEFFB5B2ADFFB2AFAAFFB2AE + A9FFB3B0ABFFB1AFACFFAFAEACFFAEAEACFFB0B0ADFF7C7C7AB03E729DFFD4E7 + F7FF569BD3FFADA096FFAA9F98FFA79E98FFA49E99FFA59D97FF4E98D5FFCCE4 + F8FF2F6A9AFF000000000000000000000000000000000000000042759FFFAFD2 + F2FFA9D0F3FF978E87FFCECAC8FFCBC8C6FFCDCAC8FF968D87FFA8D0F4FFADD1 + F2FF3D739EFF0000000000000000000000000000000000000000315471B04378 + A2FF3D77A4FF888079FFB9B6B6FFB7B5B5FFB9B6B6FF888079FF3D77A4FF4378 + A2FF315471B00000000000000000000000000000000000000000000000000000 + 00000000000000000000807F7EFF7E7E7EFF807F7EFF00000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 00230000003300000033000000330000003300000033462F1781A56F38ED5A3C + 21B3000000330000003300000033000000240000000600000006000000002B48 + 66C0396592FF356291FF346191FF346191FF346192FF6A6E71FF5A626AFF9A6F + 47FF346293FF356292FF396593FF27405BC71B1209671A120A6B000000003967 + 94FF96A8B1FFBEB9B1FFBAB6AFFFBAB6AFFFBBB7B3FFC2A78CFF9E856BFFB07B + 44FFBDBBBAFFC0BBB5FF8999A2FF63594FFF51361B975D3E22B6000000003665 + 95FFBEB9B1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF3ECFFDCAD78FFC883 + 38FFEFEFEFFFE3E3E3FFA8825BFF936C44FF150E0755764E28BC000000003665 + 95FFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD9F + 81FFB9B9B9FFD09F69FFCC8538FFB67F44FFAD7335E648301761000000003668 + 96FFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBEBEBFFA086 + 6DFF9E958EFFE09B4DFFBAB5AEFF366896FF0000000000000000000000003769 + 97FFBAB5AEFFFFFFFFFFFFFFFFFFFCFBFBFFFCFBFBFFF2F2F2FFADAEB0FFCC84 + 37FFB98B5EFFFFFFFFFFBAB5AEFF376997FF000000000000000000000000386B + 9AFFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFF1F1F1FFABABABFFD4D4D5FFC4BD + B5FFCACBCFFFFFFFFFFFBAB5AEFF386B9AFF000000000000000000000000396C + 9BFFBAB5AEFFFFFFFFFFFFFFFFFFFFFFFFFFB9B8B8FFD1D0CFFFE5E5E4FFCCCA + CAFFC7C6C6FFFFFFFFFFBAB5AEFF396C9BFF000000000000000000000000396D + 9CFFBAB5AFFFFFFFFFFFFFFFFFFFFFFFFFFFCAC9C8FFE6E5E5FFF9F9F9FFD7D5 + D4FFC1C0BEFFFFFFFFFFBAB5AFFF396D9CFF0000000000000000000000003A6F + 9EFFB9B5AEFFFFFFFFFFFFFFFFFFFFFFFFFFCDCCCAFFFBFBFBFFFFFFFFFFDDDB + DAFFC1C1BFFFFFFFFFFFB9B5AEFF3A6F9EFF0000000000000000000000003B70 + 9FFFBAB4ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9D7 + D7FFB5B3B1FFFFFFFFFFBAB4ADFF3B709FFF0000000000000000000000003C73 + A0FFBCB6AEFFFFFFFFFFFFFFFFFF999592FF9B9895FF9B9895FF9B9895FFB4B2 + B0FFFFFFFFFFFFFFFFFFBCB6AEFF3C73A0FF0000000000000000000000004076 + A2FFC5CACFFFBCB7AFFFBBB7B1FF908B88FFCAC8C7FFC9C7C6FFCAC8C7FF908B + 88FFBBB7B1FFBCB7AFFFC5CACFFF4076A2FF0000000000000000000000003154 + 71B04278A4FF3E77A4FF3A76A7FF867F7AFFB8B5B5FFB5B3B3FFB8B5B5FF867F + 7AFF3A76A7FF3E77A4FF4278A4FF315471B00000000000000000000000000000 + 000000000000000000000000000000000000979796FF959595FF979796FF0000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000002300000033000000230000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000006236C1008C4BFF006135C00000000000000000000000000000 + 0000000000000000000000000033000000330000003300000000000000000000 + 000000000000008B4AFF0CDCA5FF019F60FF0000003300000021000000000000 + 000000000000000000334780ACFF487FAAFF4880AAFF00000033000000300000 + 003300000033008947FF1DDBABFF049C60FF008C4AFF005E31B9000000330000 + 0033000000333C81B5FF729EC0FF1FADFFFF427D9AAE4490C5FF008447F2008A + 49FF008846FF008440FF2DDAB2FF059A5CFF008744FF008A46FF2D875CFF8C7C + 80FF807A78FF9D9691FFB0A49CFFA0C3D8FFB0ECFFFF3F90C6FF008B4AFF45E3 + C6FF42E0C1FF40DCBDFF3DD9B8FF3EDBBAFF41DEBFFF41E2C3FF008840FFFFE7 + EEFFBFB4B6FF817B7AFFBBB7B4FFF4ECE5FFBFDEEAFF3F92CAFF006033AF0F9F + 67FF0D9D63FF0A995EFF50DDC4FF19A774FF08975BFF0A9A5FFF00853FFF0085 + 3CFF49AC7FFFE1D7DAFF837E7CFFDED6CFFF358FCBFF00000000000000000000 + 0000008B47FF008741FF65E3D3FF0E985EFF3EDCBBFF3FDEBEFF3EE0C0FF39E0 + C2FF00813AFFFFEDD7FFB8B4B5FF918883FF0000000000000000000000000000 + 0000358A63FF008941FF79E9E3FF129A65FF51E0C7FF00823DFF00823FFF0080 + 3FFF4CA964FFFFE5A0FFECEBF0FF86827FFF0000000000000000000000000000 + 00009C898FFF1A9E64FF00863FFF00853FFF63E4D6FF00823EFFFFE9A4FFFFEB + A5FFFFE69FFFFBD88CFFF4F7FCFF868483FF0000000000000000000000000000 + 0000908889FFFFFFFFFFFFAC64FF008641FF73E9E4FF00813CFFFFE2AAFFFAD9 + 94FFF5D58FFFF1CD81FFF8FCFFFF898685FF0000000000000000000000000000 + 00008D8A89FFFDFCFFFFF1AE6EFF0C914FFF00813BFF48A770FFFFDFB4FFF2D3 + 9DFFEDC780FFEEC887FFF7F9FEFF8C8988FF0000000000000000000000000000 + 0000605E5DAAC9C9CBFFFBE5D3FFF1B072FFFFF2E3FFFFF0DEFFF8E2C3FFF1D3 + A6FFE7B96FFFF8ECDAFFC8C7C9FF605E5DAA0000000000000000000000000000 + 00000808070D908E8CFFF2F5F9FFFAE7D4FFE2A866FFE2AE6DFFE3B373FFE4B4 + 71FFFBECDAFFF1F5F8FF908E8CFF0808070D0000000000000000000000000000 + 0000000000001211111E9A9998FFCED0D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFCED0D2FF92908EFF1211111E000000000000000000000000000000000000 + 000000000000000000000808080D656361A9959291FF959291FF959291FF9592 + 91FF656361A90808080D00000000000000000000000000000000} + end> + end + object imgIcons32: TcxImageList + Height = 32 + Width = 32 + FormatVersion = 1 + DesignInfo = 11534816 + ImageInfo = < + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0001000000060000000E00000014000000160000001600000016000000160000 + 001600000016000000140000000E000000060000000100000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000020000 + 000D0000001F000000320000003E000000420000004300000043000000430000 + 0043000000420000003E000000320000001F0000000D00000002000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000000A0000 + 00272524216A757169C4A9A398F8AFA99DFFAEA99CFFAEA89CFFAEA89CFFAEA8 + 9CFFAFA99DFFA9A398F8757169C42524216A000000270000000A000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000134F4C + 4793B4ADA2FFC2BBB0FFD0C7BEFFDFD5CDFFE6DCD4FFEDE1DBFFEFE4DDFFE7DD + D6FFE1D6CFFFD1C9BFFFC3BCB1FFB4AEA2FF4F4C479300000013000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000016B2AC + 9FFFC7C0B6FFCFC7BEFFD5CCC3FFDBD2C9FFE2D8D0FFE9DDD7FFEBE0D9FFE3D9 + D2FFDDD3CBFFD6CEC5FFD1C9C0FFC9C2B9FFB1AB9FFF00000016000000000000 + 0001000000030000000500000005000000050000000500000005000000050000 + 000500000005000000050000000500000005000000050000000400000019B5AC + 9DFFC9C1B8FFCDC5BCFFD4CBC2FFDAD1C8FFE1D7CFFFE7DCD5FFEADFD8FFE2D8 + D1FFDCD2CAFFD5CDC4FFCFC7BEFFCAC3B9FFB1AB9FFF00000016000000050000 + 00120000001D0000001F0000001F0000001F0000001F0000001F0000001F0000 + 001F0000001F0000001F0000001F0000001F0000001F0000001E0000002CB9AC + 9AFFC9BFB6FFCBC3BAFFD2C9C0FFD8CFC5FFDFD5CDFFE9DFD8FFE9DDD6FFE0D6 + CFFFDAD0C8FFD3CBC2FFCEC6BDFFC9C2B7FFB0AA9EFF00000014000000100000 + 00320F212E6D0E1E2A6C0E1E2A6C0E1E2A6C0E1E2A6C0E1E2A6C0E1E2A6C0E1E + 2A6C0E1E2A6C0E1E2A6C0E1E2A6C0E1E2A6C0E1E2A6C0C1D276A08141D63BCAB + 98FFCBC2B7FFE3DDD4FFF5EFE9FFFFFBF4FFFEFAF3FFFDF9F2FFFDF9F2FFFDFA + F3FFFFFBF4FFF6F0E8FFE4DDD5FFCBC4BAFFB0AA9EFF0000001000000016418F + C4F24498D2FF4094D0FF3E92CEFF3E92CEFF3E92CEFF3E92CEFF3E92CEFF3E92 + CEFF3E92CEFF3E92CEFF3E92CEFF3E92CEFF3E92CEFF3D92CFFF3691D3FF84A0 + AEFFFDF3EBFFD7D0C8FFC6BEB6FFBEB5ABFFC2B9AFFFC7BCB4FFC8BEB5FFC3B9 + B1FFBFB6ACFFC7BFB7FFD8D1C9FFF9F5EDFF6F6B64B500000010000000164498 + D2FF3F93CFFFA8FBFFFF9AF4FFFF95F3FFFF95F3FFFF95F3FFFF95F3FFFF95F3 + FFFF95F3FFFF95F3FFFF95F3FFFF95F3FFFF95F3FFFF94F3FFFF90F6FFFFA7C6 + C3FFA5988CFFB6AEA3FFC8BFB6FFD8CFC7FFE3D9D1FFEADED8FFECE1DAFFE4DA + D3FFDAD1C9FFCAC1B8FFB8AFA6FFA49C92FF706C64B800000014000000164397 + D1FF5DB3DFFF82D3F2FF9AEFFFFF8AEAFFFF8BEAFFFF8BEAFFFF8BEAFFFF8BEA + FFFF8BEAFFFF8BEAFFFF8BEAFFFF8BEAFFFF8BEAFFFF8AEAFFFF87EFFFFFB7A5 + 94FFCAC0B6FFCEC7BDFFD5CCC3FFDAD1C8FFE1D7CFFFE8DCD6FFEADFD8FFE2D8 + D1FFDCD2CAFFD6CEC5FFD1C9C0FFCBC4BAFFB1AB9FFF00000016000000164195 + D0FF7CD3F2FF5FB2E0FFA5EFFFFF86E6FDFF87E5FDFF87E5FDFF87E5FDFF87E5 + FDFF87E5FDFF87E5FDFF87E5FDFF87E5FDFF87E5FDFF86E6FFFF82EAFFFFB5A5 + 94FFC8BEB5FFCBC3BAFFD2C8BFFFD8CEC5FFDFD5CDFFE9DED8FFE9DDD6FFE0D6 + CFFFDACFC7FFD3CBC2FFCEC6BDFFC9C2B8FFB0AB9EFF00000016000000164094 + D0FF96ECFEFF4096D1FFA6F0FFFF8FE6FEFF83E3FDFF84E3FDFF84E3FDFF84E3 + FDFF84E3FDFF84E3FDFF84E3FDFF84E3FDFF84E3FDFF83E4FFFF7FE8FFFFB5A4 + 94FFC8C0B6FFE1DBD2FFF3EDE6FFFDF9F2FFFCF8F1FFFBF7F0FFFBF7F0FFFCF8 + F1FFFDF8F2FFF4EEE6FFE2DBD3FFCAC3B9FFAFAA9DFF00000016000000163F93 + CFFFA6F6FFFF459DD5FF8EDCF7FF9CE9FFFF7FE1FCFF81E1FCFF81E1FCFF81E1 + FCFF81E1FCFF81E1FCFF81E1FCFF81E1FCFF81E1FCFF80E2FEFF7BE6FFFFB4A3 + 92FFFCF4EEFFEEE7DEFFE3DCD3FFDDD5CCFFDDD5CBFFDDD4CBFFDDD4CBFFDDD4 + CBFFDDD5CBFFE3DCD3FFEEE7DEFFFBF6EFFFAEA89CFF00000013000000163E92 + CFFFA6F5FFFF5FBBE6FF69BBE5FFAFEFFFFF7DE0FCFF7CDFFCFF7DDFFCFF7DDF + FCFF7DDFFCFF7DDFFCFF7DDFFCFF7DDFFCFF7DDFFCFF7CE0FEFF78E3FFFFB7A2 + 91FFE4D6CAFFE2D7CDFFE0D6CCFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5 + CBFFDFD5CBFFDFD6CCFFE2D8CDFFE2D8CEFFAFA99DFF0000000A000000163E92 + CFFFA9F3FFFF76D7F6FF429BD5FFB0EEFFFF8BE2FBFF78DDFBFF7ADDFBFF7ADD + FBFF7ADDFBFF7ADDFBFF7ADDFBFF7ADDFBFF7ADDFBFF7ADDFCFF77E0FFFF91C6 + D2FFBDAB9AFFD6C7BAFFE3D8CDFFF0E6DBFFF0E6DBFFF0E6DBFFF0E6DBFFF0E6 + DBFFF0E6DBFFE3D8CDFFD5C9BBFFBAB0A1FF4E4A447A00000002000000163D92 + CEFFAAF2FFFF81E6FFFF3F96D0FF97DFF8FFA1E7FEFF72DAFAFF75DBFAFF76DB + FAFF76DBFAFF76DBFAFF76DBFAFF77DBFAFF77DBFAFF77DBFAFF76DCFCFF73DE + FFFF82D0E7FF9BBABCFFACABA0FFB5A392FFB4A392FFB4A392FFB4A392FFB4A3 + 92FFB4A392FFB4AEA1FF9EB1B6FF395363A20000001200000000000000163D92 + CEFFAEF0FFFF7EE4FFFF53ADDEFF73C4EBFFBCEFFEFF6CD8FAFF6ED8FAFF6FD8 + FAFF6FD8FAFF6FD9FAFF70D9FAFF73D9FAFF74D9FAFF74D9FAFF74D9FAFF73DA + FCFF71DBFDFF6FDCFFFF6EDDFFFF6DDDFFFF6DDDFFFF6DDDFFFF6DDDFFFF6DDD + FFFF6CDCFFFF82E3FFFFB5ECFFFF3582B9E70000001A00000002000000163D92 + CFFFB1F1FFFF7AE1FDFF68C7EFFF47A4DAFFC6F4FFFFC3F2FFFFC4F2FFFFC4F2 + FFFFC4F2FFFFC5F2FFFFC7F2FFFF70D8FBFF71D7FAFF71D6F9FF71D6F9FF71D6 + F9FF71D6F9FF71D6FAFF70D6FAFF70D7FAFF70D7FBFF70D7FBFF70D7FBFF70D7 + FBFF6FD6FAFF6BD6FBFFBCF1FFFF66ADDBFF0306092F00000008000000163D92 + CFFFB5F1FFFF77DDFCFF7CE0FEFF52ACDEFF378BCAFF368BCBFF368BCBFF368B + CBFF368BCBFF368BCBFF388BCBFF4FA9DDFF70D8FCFF6FD6FAFF6DD4F9FF6DD4 + F9FF6CD4F9FF6CD4F9FF6CD4F9FF6CD4F9FF6CD4F9FF6CD4F9FF6CD4F9FF6CD4 + F9FF6CD3F9FF68D2F9FFA1E7FDFF93CAE9FF1533477B00000010000000163D92 + CFFFB8F1FFFF74DBFAFF77DCFBFF7ADFFCFF7AE0FDFF7AE1FDFF7BE1FDFF7BE1 + FDFF7BE1FDFF7BE1FDFF7CE2FEFF54ACDEFF4FA8DDFF6AD5FBFF65D2F9FF63D0 + F8FF62D0F8FF62D0F8FF62D0F8FF62D0F8FF62D0F8FF62D0F8FF62D0F8FF62D0 + F8FF62D0F8FF60D0F8FF7FDBFBFFC8EDFBFF2C6B98CA00000013000000163C92 + CFFFBDF2FFFF71DAFAFF74DAFAFF75DBFAFF76DBFBFF76DBFBFF76DBFBFF76DB + FBFF76DBFBFF76DCFBFF77DCFCFF79DFFDFF52ACDEFF4AA6DDFFDAF9FFFFD6F7 + FFFFD5F6FFFFD5F6FFFFD5F6FFFFD5F6FFFFD5F6FFFFD5F6FFFFD5F6FFFFD5F7 + FFFFD5F7FFFFD5F7FFFFD4F7FFFFD9FCFFFF3D94D0FF0000000D000000163C92 + CFFFC0F3FFFF6ED8FAFF72D8FAFF73D8FAFF73D8FAFF73D8FAFF73D8FAFF73D8 + FAFF73D8FAFF73D8FAFF73D8FAFF74DAFBFF76DDFDFF51ABDDFF388CCBFF368C + CBFF368CCBFF368CCBFF368CCBFF368CCBFF368CCBFF368BCBFF358BCBFF358C + CCFF3890CEFF3A91CEFF3B92CFFF3D94D0FF2E6891B400000004000000163C92 + CFFFC4F3FFFF6CD6F9FF70D7F9FF71D7F9FF71D7F9FF71D7F9FF71D7F9FF71D7 + F9FF71D7F9FF71D7F9FF71D7F9FF71D7F9FF72D9FAFF73DAFBFF74DCFCFF74DC + FCFF74DCFCFF74DCFCFF74DCFCFF74DCFCFF74DCFCFF73DCFCFF70DCFDFFBDF3 + FFFF3D93CFFF0000001600000000000000000000000000000000000000163B92 + CFFFC9F5FFFF69D4F9FF6ED5F9FF6FD5F9FF6FD5F9FF6FD5F9FF6FD5F9FF6FD5 + F9FF6ED5F9FF6ED5F9FF6DD5F9FF6DD5F9FF6ED5F9FF6ED5F9FF6ED5FAFF6ED5 + FAFF6ED5FAFF6ED5FAFF6ED5FAFF6ED5FAFF6ED5FAFF6DD5FAFF69D5FAFFC7F5 + FFFF3C92CFFF0000001600000000000000000000000000000000000000163B92 + CFFFCFF5FFFF67D3F8FF6CD4F8FF6DD4F8FF6DD4F8FF6DD4F8FF6DD4F8FF6BD4 + F8FF68D3F8FF66D2F8FF66D2F8FF66D2F8FF66D2F8FF66D2F8FF66D2F8FF66D2 + F8FF66D2F8FF66D2F8FF66D2F8FF66D2F8FF66D2F8FF65D2F8FF62D2F9FFD5F7 + FFFF3B92CFFF0000001500000000000000000000000000000000000000163B92 + CFFFD3F6FFFF63D1F8FF68D2F8FF69D3F8FF69D3F8FF69D3F8FF69D2F8FF66D1 + F8FFA8E7FDFFDAF7FFFFDAF8FFFFDAF8FFFFDAF8FFFFDAF8FFFFDAF8FFFFDAF8 + FFFFDAF8FFFFDAF8FFFFDAF8FFFFDAF8FFFFDAF8FFFFDAF8FFFFD9F9FFFFDEFD + FFFF3D94D0FF0000000D00000000000000000000000000000000000000153B92 + CFFFD7F8FFFF5DCFF9FF60CFF8FF61CFF8FF61CFF8FF61CFF8FF61CFF8FF5ECF + F8FFBCEEFFFF348CCCFF3991CEFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3A92 + CFFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3D94 + D0FF2E6891B400000004000000000000000000000000000000000000000D3D94 + D0FFDBFCFFFFD7F8FFFFD8F7FFFFD8F7FFFFD8F7FFFFD8F7FFFFD8F7FFFFD8F8 + FFFFDBFCFFFF3C93D0FF0000000D000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000042E68 + 91B43D94D0FF3B92CFFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3A92CFFF3B92 + CFFF3D94D0FF2E6890B300000004000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000050000001000000016000000160000001600000016000000160000 + 0016000000110000000800000004000000050000000B00000013000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 001600000016000000150000000D000000040000000000000000000000000000 + 00050000001A0000003500000042000000430000004300000043000000430000 + 00430000003A000000270000001E000000210000002D0000003C000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 0043000000430000003F0000002E000000150000000400000000000000000000 + 001000000035A87716F0B68116FFB68116FFB58013FFB37A06FFD9D2D2FFD7CD + C5FFD8CDC3FF2A29286931312F6A2F2F2D6A5D4F3696D9CFC8FFD7CCC4FFD7CC + C3FFD7CCC3FFD7CCC3FFD7CCC3FFD7CCC3FFD7CCC5FFD9D2D2FFB37A06FFB580 + 13FFB68116FFB68116FF7C5810C50000002E0000000D00000000000000000000 + 0016A87716EFF6CD89FFF3C883FFF2C782FFF2C680FFF0C173FFFFFFFFFFFFF3 + E9FFFFFFFFFF535352FF877E78FF877F79FF827A76FFFFFFFFFFFFF2E8FFFFF1 + E7FFFFF1E7FFFFF1E7FFFFF1E7FFFFF1E7FFFFF1E9FFFFFFFFFFF0C173FFF2C6 + 80FFF2C782FFF3C883FFF6CD89FF7B5810C10000001500000000000000000000 + 0016B68116FFF3C985FFEEC176FFEDC076FFECBF73FFEBBA68FFFFFFFFFFFBEE + E3FFFFFFFEFF575655FF8D847CFF8C837CFF847B74FFFFFFFDFFFBEEE2FFFAED + E1FFFAEDE1FFFAEDE1FFFAEDE1FFFAECE1FFF9ECE2FFFFFFFFFFEBBA68FFECBF + 73FFEDC076FFEEC176FFF3C985FFB68116FF0000001600000000000000000000 + 0016B68116FFF1C985FFECBD70FFEBBD70FFEBBC6EFFE9B763FFFEFFFFFFF9EA + DEFFFFFFFEFF545353FF8C837BFF8B817AFF827972FFFFFFFCFFF9EADEFFF7E9 + DEFFF7E9DEFFF7E9DEFFF7E9DEFFF7E9DEFFF7E8DEFFFEFFFFFFE9B763FFEBBC + 6EFFEBBD70FFECBD70FFF1C985FFB68116FF0000001600000000000000000000 + 0016B68116FFF1C986FFEABB6CFFEABB6CFFEABA6AFFE8B55EFFFDFEFFFFF7E8 + DBFFFFFFFCFF515050FF898078FF897E77FF80756EFFFFFFFAFFF7E8DBFFF5E7 + DBFFF5E7DBFFF5E7DBFFF5E7DBFFF5E7DBFFF5E7DBFFFDFEFFFFE8B55EFFEABA + 6AFFEABB6CFFEABB6CFFF1C986FFB68116FF0000001600000000000000000000 + 0016B68116FFF3C988FFEBB967FFEAB968FFEAB866FFE8B35AFFFCFDFFFFF3E4 + D8FFFFFFFBFF4D4C4DFF877D75FF857C74FF7D726BFFFFFEF9FFF3E4D8FFF2E4 + D8FFF2E4D8FFF2E4D8FFF2E4D8FFF2E4D8FFF1E3D8FFFDFDFFFFE8B35AFFEAB8 + 66FFEAB968FFEBB967FFF3C988FFB68116FF0000001600000000000000000000 + 0016B68116FFF1CB8AFFE9B763FFE9B764FFE9B662FFE7B156FFFCFCFFFFF1E2 + D6FFFFFFF8FF484748FF827871FF817871FF786E67FFFFFDF7FFF1E2D6FFF0E2 + D6FFF0E2D6FFF0E2D6FFF0E2D6FFF0E2D6FFEFE1D6FFFCFCFFFFE7B156FFE9B6 + 62FFE9B764FFE9B763FFF1CB8AFFB68116FF0000001600000000000000000000 + 0016B68116FFF2CB8BFFE8B55EFFE8B55FFFE8B45EFFE6AF52FFFCFCFFFFEEDE + D3FFFCF1E9FF87807BFF424446FF414346FF837E7AFFFCF1E9FFEFDFD3FFEEDE + D2FFEEDFD3FFEEDFD3FFEEDFD3FFEEDED2FFEDDED2FFFCFCFFFFE6AF52FFE8B4 + 5EFFE8B55FFFE8B55EFFF2CB8BFFB68116FF0000001600000000000000000000 + 0016B68116FFF3CB8EFFE7B25BFFE7B35DFFE7B25BFFE6AE50FFFBFCFFFFE9DC + D1FFEDDED1FFF3E3D6FFF7E8DAFFF7E8DAFFF2E3D6FFEDDED1FFEADCCFFFE9DC + CFFFE9DCCFFFE9DCCFFFE9DCCFFFE9DBCFFFE9DCD1FFFBFDFFFFE6AE50FFE7B2 + 5BFFE7B35DFFE7B25BFFF3CB8EFFB68116FF0000001600000000000000000000 + 0016B68116FFF3CC8FFFE7B056FFE7B158FFE7B158FFE7AE51FFE7CCA6FFFAFC + FFFFFAFAFFFFFBFBFFFFFCFCFFFFFCFCFFFFFBFBFFFFFAFAFFFFFAFAFFFFFAFA + FFFFFAFAFFFFFAFAFFFFFAFAFFFFFAFAFFFFFAFCFFFFE7CCA6FFE7AE51FFE7B1 + 58FFE7B158FFE7B056FFF3CC8FFFB68116FF0000001600000000000000000000 + 0016B68115FFF3CE92FFE6AE52FFE6AF54FFE6AF55FFE6AE52FFE5AC4DFFE5AA + 48FFE4A946FFE4A946FFE4A946FFE4A946FFE4A946FFE4A946FFE4A946FFE4A9 + 46FFE4A946FFE4A946FFE4A946FFE4A946FFE5AA48FFE5AC4DFFE6AE52FFE6AF + 55FFE6AF54FFE6AE52FFF3CE92FFB68115FF0000001600000000000000000000 + 0016B68115FFF3CF95FFE5AB4DFFE5AD50FFE5AD51FFE5AC4FFFE5AB4EFFE4AB + 4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB + 4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB4CFFE4AB4CFFE5AB4EFFE5AC4FFFE5AD + 51FFE5AD50FFE5AB4DFFF3CF95FFB68115FF0000001600000000000000000000 + 0016B68115FFF3D198FFE4A948FFE4AA4BFFE3A94AFFE2A643FFE1A43EFFE1A3 + 3CFFE1A33CFFE1A33CFFE1A33CFFE1A33CFFE1A33CFFE1A33CFFE1A33CFFE1A3 + 3CFFE1A33CFFE1A33CFFE1A33CFFE1A33CFFE1A33CFFE1A43EFFE2A643FFE3A9 + 4AFFE4AA4BFFE4A948FFF3D198FFB68115FF0000001600000000000000000000 + 0016B68115FFF3D19BFFE3A744FFE3A847FFE1A43FFFF6DEBAFFFFFFFFFFFFFE + FDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFE + FDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFEFDFFFFFFFFFFF6DEBAFFE1A4 + 3FFFE3A847FFE3A744FFF3D19BFFB68115FF0000001600000000000000000000 + 0016B68115FFF3D49FFFE3A63FFFE3A642FFE1A135FFFFFFFFFFFFF9F2FFFFF8 + EFFFFFF8EFFFFFF7EFFFFFF7EEFFFFF8EFFFFFF8EFFFFFF8EFFFFFF7EFFFFFF7 + EEFFFFF8EFFFFFF8EFFFFFF8EFFFFFF8EFFFFFF7EFFFFFF8F2FFFFFFFFFFE1A1 + 35FFE3A642FFE3A63FFFF3D49FFFB68115FF0000001600000000000000000000 + 0016B68115FFF5D5A2FFE2A33BFFE1A43DFFDF9E30FFFEFCFCFFFEF7EFFFFFFA + EFFFFFFAEFFFFFF9EEFFFFF8EEFFFFFBF1FFFFFCF2FFFFFBF0FFFFF9EEFFFFF8 + EDFFFFFAEFFFFFFBF0FFFFFAEFFFFFFAEFFFFFF8EEFFFEF6EEFFFEFBFBFFDF9E + 30FFE1A43DFFE2A33BFFF5D5A2FFB68115FF0000001600000000000000000000 + 0016B68114FFF5D6A6FFE1A136FFE0A239FFDE9C2BFFFCFBFBFFFFF9F0FF4E50 + 53FF909294FF8D8F91FFFFFBF1FF515255FF535557FF919395FF8D8F91FFFFFB + F0FF4F5153FF919295FF909294FF8F9193FF8C8D90FFFDF7EEFFFCFBFBFFDE9C + 2BFFE0A239FFE1A136FFF5D6A6FFB68114FF0000001600000000000000000000 + 0016B68014FFF6D9ABFFDF9F31FFDFA035FFDD9A27FFFBFAFAFFFCF6EDFFFFFA + EFFFFFFAEFFFFFF8EDFFFFF7EDFFFFFAF0FFFFFCF1FFFFFAEFFFFFF8EDFFFEF7 + ECFFFFFAEFFFFFFAF0FFFFFAEFFFFFF9EEFFFFF7ECFFFBF5ECFFFBFAFAFFDD9A + 27FFDFA035FFDF9F31FFF6D9ABFFB68014FF0000001600000000000000000000 + 0016B68014FFF6DCB1FFDF9C2DFFDF9E30FFDE9822FFF9F8F8FFFAF4EBFFFFF8 + EDFFFFFAEEFFFFF8EDFFFEF7ECFFFFF7ECFFFFF7ECFFFEF7ECFFFDF5EBFFFCF5 + EAFFFFF8EDFFFFFAEEFFFFF8EDFFFEF7ECFFFCF5EAFFF9F2EAFFF9F8F8FFDE98 + 22FFDF9E30FFDF9C2DFFF6DCB1FFB68014FF0000001600000000000000000000 + 0016B68014FFF8DDB4FFDE9A29FFDE9C2DFFDD961FFFF7F7F8FFFAF5ECFF5153 + 56FF555658FF939496FF919295FF919294FF919294FF909294FF8E8F92FFFEF7 + ECFF525356FF555658FF939496FF909294FF8D8F91FFF8F3EAFFF7F7F7FFDD96 + 1FFFDE9C2DFFDE9A29FFF8DDB4FFB68014FF0000001600000000000000000000 + 0016B68013FFF8E0BBFFDD9724FFDE9A29FFDC941BFFF6F5F5FFF7F1E8FFFCF5 + EAFFFFF7ECFFFDF5EAFFFCF4E9FFFBF4E8FFFBF4E8FFFBF4E8FFFAF2E8FFF9F2 + E7FFFCF5EAFFFEF6EBFFFDF5EAFFFCF4E9FFF9F2E7FFF6EFE7FFF6F5F5FFDC94 + 1BFFDE9A29FFDD9724FFF8E0BBFFB68013FF0000001600000000000000000000 + 0016B68013FFF9E2C0FFDD961FFFDF9925FFDC9217FFF4F4F5FFF5F0E7FFFAF4 + E9FFFCF6EAFFFAF4E9FFF9F3E7FFF8F1E7FFF7F1E6FFF9F3E8FFFAF3E8FFF9F2 + E7FFF8F1E7FFF7F1E7FFFAF3E9FFF9F3E8FFF7F1E6FFF4EEE6FFF4F4F5FFDC92 + 17FFDF9925FFDD961FFFF9E2C0FFB68013FF0000001600000000000000000000 + 0016B68012FFF9E5C5FFDF951BFFA97218FFDF9213FFF2F3F3FFF5F0E7FF5354 + 57FF565759FF949597FF929395FF8F9193FFF8F1E7FF515355FF939496FF9293 + 95FF8F9193FFF8F1E7FF515355FF929496FF8F9092FFF3EEE5FFF2F2F3FFDF92 + 13FFA97218FFDF951BFFF9E5C5FFB68012FF0000001600000000000000000000 + 0016B67F12FFFBE9CCFFE09417FF7E540EFFE0910FFFF1F2F3FFF1ECE4FFF6EF + E5FFF7F1E6FFF6F0E4FFF5EEE3FFF4EDE2FFF3EDE2FFF5EFE4FFF6EFE4FFF5EE + E3FFF4EDE2FFF3EDE2FFF5EFE4FFF5EFE4FFF3EDE2FFF0EBE3FFF1F1F2FFE091 + 0FFF7E540EFFE09417FFFBE9CCFFB67F12FF0000001600000000000000000000 + 0016B57F12FFFCECD1FFDE9010FF7B5008FFDE8E09FFEEF0F0FFEDE9E0FFEDE8 + DEFFEEE9DEFFEDE8DEFFEDE8DEFFEDE8DDFFECE8DDFFEDE8DEFFEDE8DEFFEDE8 + DEFFEDE8DDFFECE8DDFFEDE8DEFFEDE8DEFFEDE8DDFFECE9E0FFEEF0F0FFDE8E + 09FF7B5008FFDE9010FFFCECD1FFB57F12FF0000001500000000000000000000 + 0010B68013FFFDEED8FFD98904FFDB8C09FFD88600FFEDECECFFEBE6DEFFEBE5 + DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5 + DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE5DCFFEBE6DEFFEDECECFFD886 + 00FFDB8C09FFD98904FFFDEED8FFB68013FF0000000D00000000000000000000 + 0005A87715EBF1C680FFFDECCFFFFDEBCEFFFCE8C6FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE8 + C6FFFDEBCEFFFDECCFFFF1C680FF7C580FB30000000400000000000000000000 + 000000000005A77615EAB68013FFB57F12FFB57E10FFB57D0CFFB57C0AFFB57C + 09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C + 09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C09FFB57C0AFFB57D0CFFB57E + 10FFB57F12FFB68013FF7C580FB0000000040000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 00000000000000000000000000040000000D0000001500000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 0016000000160000001600000016000000150000000D00000004000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000D0000002B0000003F00000043000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 00430000004300000043000000430000003F0000002B0000000D000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000100000016906C3FC4CD9A58FFCD9957FFCD9957FFCD98 + 55FFCE964EFFAFB4BAFFB1B1B2FFB1B1B1FFB1B1B1FFB1B1B1FFB1B1B1FFB1B1 + B1FFB1B1B1FFB1B1B1FFB1B1B1FFB2B2B2FF7D7D7DC400000015000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00030000000B000000150000002AD19B55FFECDEBBFFE6D4AAFFE6D4A9FFE6D3 + A8FFE6D1A1FFFBFEFFFFFBFCFBFFFBFBFAFFFBFBFAFFFBFBFAFFFBFBFAFFFBFB + FAFFFBFBFAFFFBFBFAFFFCFCFBFFFFFFFFFFB3B3B4FF0000001C000000030000 + 0000000000000000000000000000000000000000000000000000000000040000 + 00130000002B0000003E16161666D19951FFEBDAB5FFE2C998FFE2C897FFE2C9 + 97FFE3C894FFF5F8FCFFF6F7F6FFF5F5F4FFF5F5F4FFF5F5F4FFF5F5F4FFF5F5 + F4FFF5F5F4FFF6F6F5FFF6F6F5FFFCFCFBFFB3B3B4FF000000320000001C0000 + 00150000000D00000004000000000000000000000000000000000000000D0000 + 002E4F4C46A8969188FF9E9C97FFD0984FFFEAD7B0FFFFFFFFFFFFFFFFFFFFFF + FFFFE1C38DFFF3F6FAFFAAAAAAFFD5D5D4FFF3F3F2FFBFBFBEFFD5D5D4FFD4D4 + D3FFF4F4F3FFAAAAA9FFD5D5D4FFFBFBFAFFB2B2B4FF45423CA1000000450000 + 003F0000002B0000000D00000000000000000000000000000000000000156764 + 5DC1A09B91FFC8C1B8FFD1CCC9FFCE964DFFEBD8B2FFDFC28CFFDFC18BFFE0C2 + 8CFFE0C289FFF1F4F8FFF3F4F3FFF3F3F2FFF2F2F1FFF2F2F1FFF2F2F1FFF3F3 + F2FFF2F3F2FFF3F3F2FFF2F2F1FFFAFAF9FFB0B0B1FFB2B3B4FFB2B2B2FFB2B2 + B2FF7D7D7DC40000001500000000000000000000000000000000000000169994 + 89FFC8C1B7FFCBC5BCFFCEC9C6FFCD954CFFECD8B3FFDEBF85FFDEBE85FFDEBF + 86FFDFBE83FFEFF1F5FFBBBCBCFFD2D0CFFFD1D0CFFFD0CFCEFFF0EFEEFFA8A8 + A7FFD2D1D0FFD1D0CFFFD0CFCEFFF8F8F7FFADADADFFFFFFFEFFFCFCFBFFFFFF + FFFFB2B2B2FF0000001600000000000000000000000000000000000000169993 + 89FFCAC2B9FFCAC4BAFFCDC8C5FFCC944BFFEBD8B3FFFFFFFFFFFFFFFFFFFFFF + FFFFDEBA7DFFEDEEF3FFEEEEEEFFEFEEEDFFEEEDECFFEDECEBFFEEEDECFFEFEE + EDFFEFEEEDFFEEEDECFFEDECEBFFF7F7F6FFADADADFFFBFBFAFFF7F7F6FFFCFC + FBFFB1B1B1FF0000002700000016000000150000000D00000004000000169A94 + 8AFFC9C1B8FFC9C3BAFFCCC8C4FFCC944BFFEDD9B7FFDCB97BFFDCB97BFFDDBA + 7DFFDDB97AFFEBEDF1FFB9B8B8FFCDCCCBFFCCCBCAFFEBEAE9FFB9B8B7FFCECD + CCFFCDCCCBFFCDCCCBFFCCCBCAFFF8F7F7FFAEAEAEFFAEAEADFFD6D6D5FFFBFB + FAFFB1B1B1FF00000048000000430000003F0000002B0000000D000000169A95 + 89FFC8C0B7FFC7C1B8FFD4D1CEFFCC934BFFEDDBB9FFDBB575FFDBB575FFDBB6 + 77FFDCB674FFE9EBF0FFEBEAEAFFEAE9E9FFE9E8E7FFE9E8E7FFEAE9E8FFEAE9 + E8FFEAE9E8FFEBEAE9FFE9E8E7FFF7F7F6FFAEAEAEFFF7F7F6FFF3F3F2FFFAFA + F9FFAFAFAFFFB1B1B2FFB1B1B1FFB2B2B2FF7D7D7DC400000015000000149A95 + 8AFFD3CCC3FFF6F1ECFFEEEDECFFCC934AFFEEDBB8FFFFFFFFFFFFFFFFFFFFFF + FFFFDBB16DFFE7E9EDFFA1A1A1FFCAC9C8FFE7E6E5FFB5B4B3FFCAC9C8FFC9C8 + C7FFE7E6E5FFA1A09FFFC9C8C7FFF7F7F6FFAEAEAEFFD4D3D2FFD1D0CFFFF8F8 + F7FFADADADFFFFFFFEFFFCFCFBFFFFFFFFFFB2B2B2FF00000016000000109B95 + 8BFFF2ECE4FFB7B0A8FFA39E99FFCD954CFFF0DEBEFFD8AD68FFD7AD68FFD9AE + 6AFFD9AE67FFE3E5E8FFE4E3E2FFE4E3E0FFE3E2E0FFE4E3E0FFE4E3E0FFE3E2 + E0FFE3E2E0FFE4E3E1FFE3E2E0FFF7F5F6FFADAEAEFFF1F0EFFFEEEDECFFF7F7 + F6FFADADADFFFBFBFAFFF7F7F6FFFCFCFBFFB1B1B1FF00000016000000106460 + 59B5B5ACA4FFB5ADA4FFC8C4C0FFCE964CFFF3E2C5FFF0DFC0FFF1DFC0FFF2DF + C0FFF2DDBAFFF4F7FBFFF4F5F5FFF5F5F3FFF6F6F3FFF6F6F4FFF5F5F4FFF5F5 + F4FFF5F5F4FFF5F5F4FFF5F5F4FFF8F7F7FFAEAEAEFFCFCECDFFCCCBCAFFF8F7 + F7FFAEAEAEFFAEAEADFFD6D6D5FFFBFBFAFFB1B1B1FF00000016000000146461 + 59B8B7AFA4FFCBC5BBFFCEC8C3FFCFA979FFCC944BFFCC944BFFCF964AFFD49A + 48FFD39641FFAEB1B1FFAEAEABFFB1B0ABFFB4B3ACFFB2B1AEFFB0B0AFFFAFAF + AFFFAEAEAFFFAFAFAFFFAFAFAFFFAFAFAFFFC5C6C6FFECEBEAFFEAE9E8FFF7F7 + F6FFAEAEAEFFF7F7F6FFF3F3F2FFFAFAF9FFB1B1B1FF0000001600000016A099 + 8FFFCAC2B9FFCAC3B9FFCDC6BEFFD0CAC4FFD4CECAFFD9D3CFFFE6DED2FF203B + DDFF8EA5FFFFFFFFFFFFFFFFFFFFFFFFFFFF294BF3FFF4F1E7FFA7A6A3FFCECD + CCFFEBE9E8FFB9B8B7FFCECCCBFFCCCBCAFFEAE9E8FFA2A1A0FFC9C8C7FFF7F7 + F6FFAEAEAEFFD4D3D2FFD1D0CFFFF9F9F8FFB1B1B1FF0000001600000016A09A + 8EFFC9C1B8FFC9C2B8FFCCC4BBFFCEC7BEFFD2C9C1FFD7CEC6FFE8DCC8FF273E + D3FFA0AFFBFF2B4AEDFF2C4BECFF2D4CEDFF2949EFFFF0EDDFFFE7E6E1FFE4E3 + E1FFE4E3E0FFE4E3E1FFE4E3E1FFE4E3E1FFE4E3E0FFE4E3E1FFE3E2E0FFF7F5 + F6FFADAEAEFFF1F0EFFFEEEDECFFF8F8F7FFB1B1B1FF0000001600000016A09A + 8FFFC8C0B7FFC7C0B6FFD4CDC4FFE4DDD6FFF2EDE5FFF8F2EAFFFFFFF5FF273D + D3FFA9B8FCFFA5B3F9FFA6B4FAFFA6B3FAFF9EACFBFFFFFEF2FFF6F6F3FFF5F4 + F4FFF6F5F4FFF6F5F4FFF5F5F4FFF5F5F4FFF5F5F4FFF5F5F4FFF5F5F4FFF8F7 + F7FFAEAEAEFFCFCECDFFCCCBCAFFF8F7F7FFB1B1B1FF0000001600000014A09A + 8EFFD3CBC3FFF6F1EAFFEFE9E3FFDAD5CDFFCAC2BAFFBEB4ABFFBDB2A1FF5E67 + BFFF283ED3FF2A3ED1FF2F3FD3FF353FD6FF2D38D5FFBAB3ACFFB1ACACFFB3AC + AEFFB8AEB0FFB4AFB0FFB0AFB0FFAFAFAFFFAEAEAFFFAFAFAFFFAFAFAFFFAFAF + AFFFC5C6C6FFECEBEAFFEAE9E8FFF7F7F6FFB1B1B1FF0000001600000010A29A + 8FFFF1EBE4FFB7AFA6FFA59C91FFABA298FFBBB2A9FFC5BBB1FFD4CBBFFFDDD1 + C0FFEDE0CCFFF8E9D5FFFFEFDDFF009E5AFF68E9C6FFFFFFFFFFFFFFFFFFFFFF + FFFF00BF8DFFF9EBEDFFA8A4A3FFCECDCCFFEBE9E8FFB9B8B7FFCECCCBFFCCCB + CAFFEAE9E8FFA2A1A0FFC9C8C7FFF7F7F6FFB1B1B1FF00000016000000106863 + 5CB5B4ACA3FFB5ADA2FFC9C1B8FFD3CBC2FFD6CDC4FFD9D0C8FFDDD3CBFFE0D7 + CEFFE5DAD0FFEBDED6FFFFE6E0FF00965FFF7CE5D1FF00BB8EFF00BA8EFF00BB + 90FF00BB8EFFF7E5E6FFE8E4E2FFE4E3E1FFE4E3E0FFE4E3E1FFE4E3E1FFE4E3 + E1FFE4E3E0FFE4E3E1FFE3E2E0FFF7F6F6FFB2B2B2FF00000015000000146964 + 5CB8B6AEA4FFCBC4BAFFCEC6BDFFD1C9C0FFD4CBC2FFD7CEC6FFDBD1C9FFDED5 + CDFFE2D8D0FFE8DCD6FFFEE3E0FF00945FFF8BE7DAFF86E4D5FF87E4D5FF86E4 + D5FF79E3D1FFFFF7FAFFF7F6F5FFF5F5F5FFF5F5F4FFF5F6F5FFF6F6F5FFF5F6 + F5FFF5F6F5FFF6F6F5FFF6F6F5FFFAF8F9FFB3B3B3FF0000000D00000016A59E + 93FFC9C1B9FFCAC3B9FFCDC5BCFFD0C8BFFFD4CBC2FFD7CEC6FFDBD1C9FFDED5 + CDFFE2D8D0FFE7DBD5FFF7E1DDFF45B290FF00945FFF009560FF009560FF0095 + 60FF00945DFFBAB1B4FFB1AFB1FFAFB0B1FFAFB0B1FFB1B1B2FFB2B2B3FFB2B2 + B2FFB2B2B2FFB1B1B2FFB2B2B2FFB3B3B3FF7D7D7DB40000000400000016A69F + 94FFC8C0B8FFC9C2B8FFCCC3BAFFCEC6BDFFD2C8BFFFD5CBC3FFD9CEC6FFDCD2 + CAFFE0D6CEFFE4D9D2FFEDDED8FFF7E1DDFFF9DFDBFFF7DCD7FFF2D8D4FFEED4 + CFFFE5D0CAFFDACCC3FFD2C8BEFFCEC5BBFFCBC2B8FFCAC1B6FFA49C8FFF0000 + 001600000000000000000000000000000000000000000000000000000016A7A0 + 93FFC7BEB6FFC6BFB4FFD2CBC2FFE2DBD3FFF0EAE2FFF3EEE7FFFDFAF3FFFDF9 + F2FFFDF8F2FFFCF8F1FFFDF8F1FFFEF8F2FFFFF9F3FFFFFAF3FFFFFAF4FFFFFA + F4FFF6EEE8FFF1EAE3FFE2DCD3FFD3CCC3FFC7BFB6FFC8BFB7FFA69E92FF0000 + 001600000000000000000000000000000000000000000000000000000016A69F + 93FFCFC8BFFFF8F4ECFFFFFAF4FFF8F2EBFFE9E2DAFFE3DAD3FFD7CFC6FFD4CB + C2FFD4CBC2FFD4CBC2FFD4CBC2FFD4CBC2FFD4CBC2FFD4CBC2FFD4CBC2FFD7CF + C6FFE3DAD3FFE9E2DAFFF8F2EBFFFFFAF4FFF4F0E8FFCFC8BFFFA69F92FF0000 + 001600000000000000000000000000000000000000000000000000000016A69E + 93FFFFFCF5FFEAE3DBFFD3C9C0FFCDC3B9FFCEC5BAFFCEC5BBFFCFC6BBFFCFC6 + BCFFCFC6BCFFCFC6BCFFCFC6BCFFCFC6BCFFCFC6BCFFCFC6BCFFCFC6BCFFCFC6 + BBFFCEC5BBFFCEC5BAFFCDC3B9FFD3C9C0FFEAE3DBFFFFFCF5FFABA499FF0000 + 001600000000000000000000000000000000000000000000000000000015A69F + 93FFECE5DDFFD5CCC2FFD5CCC2FFD6CDC3FFD7CEC3FFD7CEC4FFD7CEC4FFD7CE + C4FFD7CEC4FFD7CEC4FFD7CEC4FFD7CEC4FFD7CEC4FFD7CEC4FFD7CEC4FFD7CE + C4FFD7CEC4FFD7CEC3FFD6CDC3FFD5CCC2FFD5CCC2FFECE5DDFFA69F93FF0000 + 00150000000000000000000000000000000000000000000000000000000DA9A2 + 96FFE0D6CDFFE0D6CCFFDED5CBFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4 + CAFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4CAFFDDD4 + CAFFDDD4CAFFDDD4CAFFDDD4CAFFDED5CBFFDFD6CCFFE0D6CDFFA9A296FF0000 + 000D00000000000000000000000000000000000000000000000000000004746E + 67B2B5ADA1FFDED5CBFFE9E1D7FFE7DFD5FFE6DED4FFE5DDD3FFE5DDD3FFE4DC + D2FFE4DCD2FFE4DCD2FFE4DCD2FFE4DCD2FFE4DCD2FFE4DCD2FFE4DCD2FFE5DD + D3FFE5DDD3FFE6DED4FFE7DFD5FFE9E1D7FFE2DAD0FFB5ADA1FF746E67B20000 + 0004000000000000000000000000000000000000000000000000000000000000 + 00035A555089AAA396FFB7AEA3FFC8BFB4FFD9D1C6FFDDD5CAFFF1E9DFFFF0E8 + DEFFF0E8DEFFF0E8DEFFF0E8DEFFF0E8DEFFF0E8DEFFF0E8DEFFF0E8DEFFF1E9 + DFFFDDD5CAFFD9D1C6FFC8BFB4FFB7AEA3FFAAA296FF5A555089000000030000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000123211F3757534D858B857CD2968F84E2AAA296FFAAA2 + 96FFAAA296FFAAA296FFAAA296FFAAA296FFAAA296FFAAA296FFAAA296FFAAA2 + 96FF968F84E28B857CD257534D8523211F370000000100000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 00040000000D0000001500000016000000160000001600000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 00160000001600000016000000150000000D0000000400000000000000000000 + 000D0000002B0000003F00000043000000430000004300000043000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 004300000043000000430000003F0000002B0000000D00000000000000000000 + 0015004A22C400682EFF006730FF006731FF006832FF006832FF006832FF0068 + 32FF006832FF006930FF006E26FF0B31A7FF0B359BFF0B3598FF0B3598FF0B35 + 9BFF0B31A7FF006E26FF006930FF006832FF006832FF006832FF006832FF0068 + 32FF006731FF006730FF00682EFF004A22C40000001500000000000000000000 + 001600672DFF92DE9CFF1DAD36FF21AE3AFF23AF3AFF23AF3AFF23AF3AFF23AF + 3AFF23AF3AFF23B039FF24B830FF0B2EA6FF1075E1FF0A70DBFF0A70DBFF1075 + E1FF0B2EA6FF24B830FF23B039FF23AF3AFF23AF3AFF23AF3AFF23AF3AFF23AF + 3AFF21AE3AFF1DAD36FF92DE9CFF00672DFF0000001600000000000000000000 + 001600642AFF96E1A6FF2AB84AFF2FB84EFF30B950FF30B950FF30B950FF30B9 + 50FF30B950FF31BB4EFF33C546FF092DA6FF1C7AE1FF0B6ED9FF0B6ED9FF1C7A + E1FF092DA6FF33C546FF31BB4EFF30B950FF30B950FF30B950FF30B950FF30B9 + 50FF2FB84EFF2AB84AFF96E1A6FF00642AFF0000001600000000000000000000 + 0016006328FF9FEAB3FF38C660FF3CC663FF3DC664FF3DC664FF3DC664FF3DC6 + 64FF3DC664FF3EC863FF42D35CFF0A2DA6FF2781E1FF0D6FD8FF0D6FD8FF2781 + E1FF0A2DA6FF42D35CFF3EC863FF3DC664FF3DC664FF3DC664FF3DC664FF3DC6 + 64FF3CC663FF38C660FF9FEAB3FF006328FF0000001600000000000000000000 + 0016006328FFA6F4C0FF43D675FF45D577FF46D677FF46D677FF46D677FF46D6 + 77FF47D677FF4AD875FF51E26CFF082CA0FF3185E0FF0C6DD7FF0C6DD7FF3185 + E0FF082CA0FF51E26CFF4AD875FF47D677FF46D677FF46D677FF46D677FF46D6 + 77FF45D577FF43D675FFA6F4C0FF006328FF0000001600000000000000000000 + 001600662DFFA8FFD7FFA1FFD1FFA1FFD1FFA1FFD2FFA1FFD2FFA1FFD2FFA1FF + D2FFA2FFD0FFA9FFC8FF53C2F3FF22A2FFFF26A4FFFF27A5FFFF27A5FFFF26A4 + FFFF22A2FFFF53C2F3FFA9FFC8FFA2FFD0FFA1FFD2FFA1FFD2FFA1FFD2FFA1FF + D2FFA1FFD1FFA1FFD1FFA8FFD7FF00662DFF0000001600000000000000000000 + 00166F570EFFAE4300FFA74100FFA74100FFA74100FFA74100FFA74100FFA741 + 00FFA84100FFB33D00FF1EAAFFFF2BA9FFFF2EAAFFFF30ACFFFF30ACFFFF2EAA + FFFF2BA9FFFF1EAAFFFFB33D00FFA84100FFA74100FFA74100FFA74100FFA741 + 00FFA74100FFA74100FFAE4300FF6F570EFF0000001600000000000000000000 + 0016AA4C00FFFEB5A9FFEB6745FFEB6948FFEB6A49FFEB6A49FFEB6A49FFEB6A + 49FFED6946FFFB6435FF25B4FFFF34B2FFFF5197E1FF1073DAFF1073DAFF5197 + E1FF34B2FFFF25B4FFFFFB6435FFED6946FFEB6A49FFEB6A49FFEB6A49FFEB6A + 49FFEB6948FFEB6745FFFEB5A9FFAA4C00FF0000001600000000000000000000 + 0016A34B00FFFBC0AFFFEB7B54FFEB7E57FFEB7E58FFEB7E58FFEB7E58FFEB7E + 58FFED7D55FFFD7846FF2DBAFFFF3EBAFFFF1175DCFF157BDFFF157BDFFF1175 + DCFF3EBAFFFF2DBAFFFFFD7846FFED7D55FFEB7E58FFEB7E58FFEB7E58FFEB7E + 58FFEB7E57FFEB7B54FFFBC0AFFFA34B00FF0000001600000000000000000000 + 0016A24A00FFFFCAB8FFF28C63FFF18F65FFF18F66FFF18F66FFF18F66FFF18F + 66FFF48F64FFFF8B57FF35C1FFFF48C2FFFF1378DEFF177DE0FF177DE0FF1378 + DEFF48C2FFFF35C1FFFFFF8B57FFF48F64FFF18F66FFF18F66FFF18F66FFF18F + 66FFF18F65FFF28C63FFFFCAB8FFA24A00FF0000001600000000000000000000 + 0016A64C00FFFFDAC2FFFEA372FFFEA473FFFEA574FFFEA574FFFEA574FFFEA5 + 74FFFFA471FFFFA066FF40C6FFFF4FC7FFFF1278DFFF167DE1FF167DE1FF1278 + DFFF4FC7FFFF40C6FFFFFFA066FFFFA471FFFEA574FFFEA574FFFEA574FFFEA5 + 74FFFEA473FFFEA372FFFFDAC2FFA54B00FF0000001600000000000000000000 + 0016A94E00FFD2B1BEFFD0ADB5FFCFADB4FFCFAEB4FFCFAEB4FFCFAEB4FFCFAE + B4FFD1ADB3FFD8A6AAFF4DD1FFFF55CCFFFF72B8F1FF2490E9FF2490E9FF72B8 + F1FF55CCFFFF4DD1FFFFD8A6AAFFD1ADB3FFCFAEB4FFCFAEB4FFCFAEB4FFCFAE + B4FFCFADB4FFCFADB5FFD1B1BEFFB35200FF0000001600000000000000000000 + 00161C118AFF000099FF000092FF000091FF000091FF000091FF000091FF0000 + 91FF000090FF00008AFF5FDEFFFF5ED3FFFF5FD2FFFF61D3FFFF61D3FFFF5FD2 + FFFF5ED3FFFF5FDEFFFF00008AFF000090FF000091FF000091FF000091FF0000 + 91FF000091FF000092FF00009AFF341D68F40000001600000000000000000000 + 00160A0697FFA1C4FFFF3D75FFFF4076FFFF4177FFFF4177FFFF4177FFFF4177 + FFFF4076FFFF3E6FFFFF5BB7FFFF6AE2FFFF68DDFFFF6BDDFFFF6BDDFFFF68DD + FFFF6AE2FFFF5BB7FFFF3E6FFFFF4076FFFF4177FFFF4177FFFF4177FFFF4177 + FFFF4076FFFF3D75FFFFA1C4FFFF080599FF0000001600000000000000000000 + 00160A0493FFAAC7FFFF4D7DFFFF507FFFFF517FFFFF517FFFFF517FFFFF517F + FFFF517EFFFF507CFFFF5179FFFF0E4DB4FF96C8F5FF1080E6FF1080E6FF96C8 + F5FF0E4DB4FF5179FFFF507CFFFF517EFFFF517FFFFF517FFFFF517FFFFF517F + FFFF507FFFFF4D7DFFFFAAC7FFFF0A0493FF0000001600000000000000000000 + 0016090292FFB3CEFFFF5C89FFFF5F8AFDFF608AFDFF608AFDFF608AFDFF5F89 + FDFF5C88FDFF5B87FFFF5D88FFFF0850BCFF9ACFFBFF0D85ECFF0D85ECFF9ACF + FBFF0850BCFF5D88FFFF5B87FFFF5C88FDFF5F89FDFF608AFDFF608AFDFF608A + FDFF5F8AFDFF5C89FFFFB3CEFFFF090292FF0000001600000000000000000000 + 0016080292FFBED8FFFF6B97FFFF6D97FEFF6D98FEFF6D98FEFF6D97FEFF6A95 + FEFFA2BDFFFFC4DAFFFFC4DBFFFF93BAF3FFDEFAFFFF95D4FFFF95D4FFFFDEFA + FFFF93BAF3FFC4DBFFFFC4DAFFFFA2BDFFFF6A95FEFF6D97FEFF6D98FEFF6D98 + FEFF6D97FEFF6B97FFFFBED8FFFF080292FF0000001500000000000000000000 + 00100B0694FFD1EBFFFFCAE1FFFFCAE0FFFFCAE0FFFFCAE0FFFFC8E0FFFFC3DB + FFFFEEFCFFFFB79B7EFFBA7B2BFFB97B2FFFB8792FFFB97A2FFFB97A2FFFB879 + 2FFFB97B2FFFBA7B2BFFB79B7EFFEEFCFFFFC3DBFFFFC8E0FFFFCAE0FFFFCAE0 + FFFFCAE0FFFFCAE1FFFFD1EBFFFF0B0694FF0000000D00000000000000000000 + 0005100D8FF00B0595FF070292FF060192FF060192FF060092FF030092FF0000 + 91FF8C8DDBFFBB7F2FFFFECF86FFFACD87FFF9CC87FFFACC87FFFACC87FFF9CC + 87FFFACD87FFFECF86FFBB7F2FFF8C8DDBFF000091FF030092FF060092FF0601 + 92FF060192FF070292FF0B0595FF0C0A69B40000000400000000000000000000 + 00000000000000000000000000000000000000000000000000040000000D0000 + 001500000027BA8136FFF8CF91FFF1C076FFF0C076FFF0C076FFF0C076FFF0C0 + 76FFF1C076FFF8CF91FFBA8136FF00000027000000150000000D000000040000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000D0000002B0000 + 004000000048B7803BFFF4D099FFEAB96EFFEAB970FFEAB970FFEAB970FFEAB9 + 70FFEAB96EFFF4D099FFB7803BFF000000480000003F0000002B0000000D0000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000F86612EC2BA84 + 3FFFB6803DFFB37C3AFFF1D2A2FFE4B167FFE4B36AFFE4B36AFFE4B36AFFE4B3 + 6AFFE4B167FFF1D2A2FFB37C3AFFB7803DFFBB8440FF87612EC20000000F0000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000006B98442FFFBE2 + ACFFF7DDA7FFF5DBA6FFECC88AFFDFAB60FFDFAD63FFDFAD63FFDFAD63FFDFAD + 63FFDFAB60FFECC88AFFF6DCA6FFF8DEA7FFE0CDA1F7BA8442FF000000060000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000006B57F + 3EFFF4DAA7FFD9A455FFD9A558FFDAA75BFFDAA85DFFDAA85DFFDAA85DFFDAA8 + 5DFFDAA75BFFD9A558FFD9A455FFD6C399F2B6803EFF00000006000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0006B57F3EFFF2DAABFFD49F51FFD4A054FFD5A256FFD5A257FFD5A257FFD5A2 + 56FFD4A054FFD49F51FFEFD8ABFFB67F3EFF0000000600000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000006B6803FFFEFD9AEFFCD984BFFCE9A4EFFCF9C50FFCF9C50FFCE9A + 4EFFCD984BFFEDD8AFFFB6803FFF000000060000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000006B6803FFFEDD7B1FFC89242FFC99446FFC99446FFC892 + 42FFEED7B1FFB6803FFF00000006000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000006B7813FFFEBD7B4FFC28B39FFC28B39FFEBD7 + B4FFB7813FFF0000000600000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000006B7813FFFE9D7B8FFE9D7B8FFB781 + 3FFF000000060000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000006B88341FFB88341FF0000 + 0006000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 00040000000D0000001500000016000000160000001600000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 00160000001600000016000000150000000D0000000400000000000000000000 + 000D0000002B0000003F00000043000000430000004300000043000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 004300000043000000430000003F0000002B0000000D00000000000000000000 + 0015004A22C400682EFF006730FF006731FF006832FF006832FF006832FF0068 + 32FF006832FF006930FF006E26FF0B31A7FF0B359BFF0B3598FF0B3598FF0B35 + 9BFF0B31A7FF006E26FF006930FF006832FF006832FF006832FF006832FF0068 + 32FF006731FF006730FF00682EFF004A22C40000001500000000000000000000 + 001600672DFF92DE9CFF1DAD36FF21AE3AFF23AF3AFF23AF3AFF23AF3AFF23AF + 3AFF23AF3AFF23B039FF24B830FF0B2EA6FF1075E1FF0A70DBFF0A70DBFF1075 + E1FF0B2EA6FF24B830FF23B039FF23AF3AFF23AF3AFF23AF3AFF23AF3AFF23AF + 3AFF21AE3AFF1DAD36FF92DE9CFF00672DFF0000001600000000000000000000 + 001600642AFF96E1A6FF2AB84AFF2FB84EFF30B950FF30B950FF30B950FF30B9 + 50FF30B950FF31BB4EFF33C546FF092DA6FF1C7AE1FF0B6ED9FF0B6ED9FF1C7A + E1FF092DA6FF33C546FF31BB4EFF30B950FF30B950FF30B950FF30B950FF30B9 + 50FF2FB84EFF2AB84AFF96E1A6FF00642AFF0000001600000000000000000000 + 0016006328FF9FEAB3FF38C660FF3CC663FF3DC664FF3DC664FF3DC664FF3DC6 + 64FF3DC664FF3EC863FF42D35CFF0A2DA6FF2781E1FF0D6FD8FF0D6FD8FF2781 + E1FF0A2DA6FF42D35CFF3EC863FF3DC664FF3DC664FF3DC664FF3DC664FF3DC6 + 64FF3CC663FF38C660FF9FEAB3FF006328FF0000001600000000000000000000 + 0016006328FFA6F4C0FF43D675FF45D577FF46D677FF46D677FF46D677FF46D6 + 77FF47D677FF4AD875FF51E26CFF082CA0FF3185E0FF0C6DD7FF0C6DD7FF3185 + E0FF082CA0FF51E26CFF4AD875FF47D677FF46D677FF46D677FF46D677FF46D6 + 77FF45D577FF43D675FFA6F4C0FF006328FF0000001600000000000000000000 + 001600662DFFA8FFD7FFA1FFD1FFA1FFD1FFA1FFD2FFA1FFD2FFA1FFD2FFA1FF + D2FFA2FFD0FFA9FFC8FF53C2F3FF22A2FFFF26A4FFFF27A5FFFF27A5FFFF26A4 + FFFF22A2FFFF53C2F3FFA9FFC8FFA2FFD0FFA1FFD2FFA1FFD2FFA1FFD2FFA1FF + D2FFA1FFD1FFA1FFD1FFA8FFD7FF00662DFF0000001600000000000000000000 + 00166F570EFFAE4300FFA74100FFA74100FFA74100FFA74100FFA74100FFA741 + 00FFA84100FFB33D00FF1EAAFFFF2BA9FFFF2EAAFFFF30ACFFFF30ACFFFF2EAA + FFFF2BA9FFFF1EAAFFFFB33D00FFA84100FFA74100FFA74100FFA74100FFA741 + 00FFA74100FFA74100FFAE4300FF6F570EFF0000001600000000000000000000 + 0016AA4C00FFFEB5A9FFEB6745FFEB6948FFEB6A49FFEB6A49FFEB6A49FFEB6A + 49FFED6946FFFB6435FF25B4FFFF34B2FFFF5197E1FF1073DAFF1073DAFF5197 + E1FF34B2FFFF25B4FFFFFB6435FFED6946FFEB6A49FFEB6A49FFEB6A49FFEB6A + 49FFEB6948FFEB6745FFFEB5A9FFAA4C00FF0000001600000000000000000000 + 0016A34B00FFFBC0AFFFEB7B54FFEB7E57FFEB7E58FFEB7E58FFEB7E58FFEB7E + 58FFED7D55FFFD7846FF2DBAFFFF3EBAFFFF1175DCFF157BDFFF157BDFFF1175 + DCFF3EBAFFFF2DBAFFFFFD7846FFED7D55FFEB7E58FFEB7E58FFEB7E58FFEB7E + 58FFEB7E57FFEB7B54FFFBC0AFFFA34B00FF0000001600000000000000000000 + 0016A24A00FFFFCAB8FFF28C63FFF18F65FFF18F66FFF18F66FFF18F66FFF18F + 66FFF48F64FFFF8B57FF35C1FFFF48C2FFFF1378DEFF177DE0FF177DE0FF1378 + DEFF48C2FFFF35C1FFFFFF8B57FFF48F64FFF18F66FFF18F66FFF18F66FFF18F + 66FFF18F65FFF28C63FFFFCAB8FFA24A00FF0000001600000000000000000000 + 0016A64C00FFFFDAC2FFFEA372FFFEA473FFFEA574FFFEA574FFFEA574FFFEA5 + 74FFFFA471FFFFA066FF40C6FFFF4FC7FFFF1278DFFF167DE1FF167DE1FF1278 + DFFF4FC7FFFF40C6FFFFFFA066FFFFA471FFFEA574FFFEA574FFFEA574FFFEA5 + 74FFFEA473FFFEA372FFFFDAC2FFA54B00FF0000001600000000000000000000 + 0016A94E00FFD2B1BEFFD0ADB5FFCFADB4FFCFAEB4FFCFAEB4FFCFAEB4FFCFAE + B4FFD1ADB3FFD8A6AAFF4DD1FFFF55CCFFFF72B8F1FF2490E9FF2490E9FF72B8 + F1FF55CCFFFF4DD1FFFFD8A6AAFFD1ADB3FFCFAEB4FFCFAEB4FFCFAEB4FFCFAE + B4FFCFADB4FFCFADB5FFD1B1BEFFB35200FF0000001600000000000000000000 + 00161C118AFF000099FF000092FF000091FF000091FF000091FF000091FF0000 + 91FF000090FF00008AFF5FDEFFFF5ED3FFFF5FD2FFFF61D3FFFF61D3FFFF5FD2 + FFFF5ED3FFFF5FDEFFFF00008AFF000090FF000091FF000091FF000091FF0000 + 91FF000091FF000092FF00009AFF341D68F40000001600000000000000000000 + 00160A0697FFA1C4FFFF3D75FFFF4076FFFF4177FFFF4177FFFF4177FFFF4177 + FFFF4076FFFF3E6FFFFF5BB7FFFF6AE2FFFF68DDFFFF6BDDFFFF6BDDFFFF68DD + FFFF6AE2FFFF5BB7FFFF3E6FFFFF4076FFFF4177FFFF4177FFFF4177FFFF4177 + FFFF4076FFFF3D75FFFFA1C4FFFF080599FF0000001600000000000000000000 + 00160A0493FFAAC7FFFF4D7DFFFF507FFFFF517FFFFF517FFFFF517FFFFF517F + FFFF517FFFFF517DFFFF527AFFFF104EB5FF97C8F5FF107FE6FF107FE6FF97C8 + F5FF104EB5FF527AFFFF517DFFFF517FFFFF517FFFFF517FFFFF517FFFFF517F + FFFF507FFFFF4D7DFFFFAAC7FFFF0A0493FF0000001600000000000000000000 + 0016090292FFB3CEFFFF5C89FFFF5F8AFDFF608AFDFF608AFDFF608AFDFF608A + FDFF608AFDFF618BFEFF658CFFFF1055BBFF9FD1FAFF0D84EBFF0D84EBFF9FD1 + FAFF1055BBFF658CFFFF618BFEFF608AFDFF608AFDFF608AFDFF608AFDFF608A + FDFF5F8AFDFF5C89FFFFB3CEFFFF090292FF0000001600000000000000000000 + 0016080292FFBED8FFFF6B97FFFF6D97FEFF6D98FEFF6D98FEFF6D98FEFF6D98 + FEFF6D98FEFF6E98FEFF739BFFFF0E56C0FFA3D5FFFFDEEEFFFFDEEEFFFFA3D5 + FFFF0E56C0FF739BFFFF6E98FEFF6D98FEFF6D98FEFF6D98FEFF6D98FEFF6D98 + FEFF6D97FEFF6B97FFFFBED8FFFF080292FF0000001500000000000000000000 + 00100B0694FFD1EBFFFFCAE1FFFFCAE0FFFFCAE0FFFFCAE0FFFFCAE0FFFFCAE0 + FFFFCAE0FFFFCAE0FFFFCDE0FFFF0D58C8FFFFFFFFFF1E9755FF1E9755FFFFFF + FFFF0D58C8FFCDE0FFFFCAE0FFFFCAE0FFFFCAE0FFFFCAE0FFFFCAE0FFFFCAE0 + FFFFCAE0FFFFCAE1FFFFD1EBFFFF0B0694FF0000000D00000000000000000000 + 0005100D8FF00B0595FF070292FF060192FF070192FF070192FF070192FF0701 + 92FF070092FF060093FF040093FFF1EFFFFF007F2FFF00F0B6FF00F0B6FF007F + 2FFFF1EFFFFF040093FF060093FF070092FF070192FF070192FF070192FF0701 + 92FF060192FF070292FF0B0595FF0C0A69B40000000400000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000060000001C0000003D008A35FF00F2B8FF00DB99FF00DB99FF00F2 + B8FF008A35FF0000003D0000001C000000060000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00060000001C0000003D008F3CFF04F2BCFF00D79AFF00D59AFF00D59AFF00D8 + 9AFF04F2BCFF008F3CFF0000003D0000001C0000000600000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000060000 + 001C0000003D008A40FF0EEDC1FF00D39BFF00D19AFF00D19AFF00D19AFF00D1 + 9AFF00D49BFF0FE5BAFE008B40FF0000003C0000001B00000006000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000060000001C0000 + 003D008741FF18EAC3FF00D09BFF00CE9AFF00CE9AFF00CE9AFF00CE9AFF00CE + 9AFF00CE9AFF00D09BFF18E3BEFE008842FF0000003B0000001B000000060000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000F000000340087 + 43FF1FE6C4FF00CB9AFF00C998FF00CA99FF00CA9AFF00CA9AFF00CA9AFF00CA + 9AFF00CA99FF00CA98FF00CC9AFF1FBFA5ED008844FF000000330000000F0000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000D008C4AFF29E8 + CEFF27E2C6FF26DEC1FF16D4B1FF00C59AFF00C69BFF00C69BFF00C69BFF00C6 + 9BFF00C59AFF16D4B1FF26DFC1FF27E3C6FF2AC9B4EE008C4AFF0000000D0000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000004006235B6008A + 48FF008744FF00833DFF5CDBC5FF00C199FF00C29BFF00C29BFF00C29BFF00C2 + 9BFF00C199FF5CDBC5FF00833DFF008744FF008A48FF006235B6000000040000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000016008742FF7DE2D4FF00BC98FF00BD99FF00BD9AFF00BD9AFF00BD + 99FF00BC98FF7DE2D4FF008742FF000000160000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000015008743FF9DEAE2FF00B797FF00B898FF00B898FF00B898FF00B8 + 98FF00B797FF9DEAE2FF008743FF000000150000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000D008945FFB5F1F1FFB2EEEBFFB4EEEAFFB5EEEBFFB5EEEBFFB4EE + EAFFB2EEEBFFB5F1F1FF008945FF0000000D0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000004006134B4008945FF008742FF008742FF008742FF008742FF0087 + 42FF008742FF008945FF006134B4000000040000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000050000001000000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 0016000000160000001600000016000000150000000D00000004000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000100000003100000042000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 00430000004300000043000000430000003F0000002B0000000D000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000016A39284F3AB9787FFAA9584FFAB95 + 83FFAB9583FFAA9584FFA99584FFA99585FFA99585FFA99585FFA99585FFA995 + 85FFA99585FFA99585FFA99585FFAA9787FF786B61C400000015000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000016AB9787FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA9787FF00000016000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000016AA9584FFFFFFFFFF27D2FFFF32D3 + FFFF32D3FFFF26D2FFFFFFFFFFFFBEA07FFFBEA383FFBEA384FFBEA384FFBEA3 + 84FFBEA384FFBEA383FFBCA080FFFFFFFFFFA89584FF00000016000000000000 + 000000000000000000000000000000000000000000040000000D000000150000 + 001600000016000000160000001600000027AB9583FFFFFFFFFF27C2FFFF36C5 + FFFF36C6FFFF26C2FFFFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA89484FF00000016000000000000 + 0000000000000000000000000000000000000000000D0000002B0000003F0000 + 004300000043000000430000004300000048AB9583FFFFFFFFFF20B4FFFF31B9 + FFFF31B9FFFF20B5FFFFFFFEF8FFBFA180FFBEA385FFBFA485FFBFA485FFBFA4 + 85FFBFA485FFBEA384FFBCA081FFFFFFFFFFA89584FF00000016000000000000 + 00000000000000000000000000000000000000000015786B61C4AA9787FFA895 + 84FFA89483FFA89483FFA89584FFA99585FFA99280FFFFFFFFFF13A4FFFF24A9 + FFFF24AAFFFF14A4FFFFFFFBF4FFFBFAFCFFF9FCFFFFFAFCFFFFFAFCFFFFFAFC + FFFFFAFCFFFFF9FCFFFFF7F9FDFFFFFFFFFFA89584FF00000016000000000000 + 00000000000000000000000000000000000000000016AA9787FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA58E7CFFFFFFFFFF008FFFFF0A94 + FFFF0A94FFFF0090FFFFFFF7F3FFC0A282FFBFA486FFBFA587FFBFA587FFBFA5 + 87FFBFA587FFBFA486FFBDA182FFFFFFFFFFA89584FF00000016000000050000 + 00100000001600000016000000160000001600000027A89584FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA38D7CFFFFFFFFFFFBF4F3FFFFF8 + F6FFFFF8F6FFFCF7F7FFF3F4FAFFF0F5FEFFF0F6FFFFF0F6FFFFF0F6FFFFF0F6 + FFFFF0F6FFFFEFF5FFFFEEF3FAFFFFFFFFFFA89584FF00000016000000100000 + 00310000004200000043000000430000004300000048A89484FFFFFFFFFFBB9F + 7FFFBDA182FFBDA283FFBEA383FFC1A687FFA38F7FFFFFFFFFFFEAAE59FFECB3 + 61FFEDB463FFECB463FFEBB364FFEAB365FFEAB465FFEAB465FFEAB465FFEAB4 + 65FFEAB465FFEAB363FFE8AE5AFFFFFFFFFFA89585FF0000001600000016A392 + 84F3AA9787FFA89586FFA89586FFA99686FFA99687FFA69281FFFFFFFFFFFDFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA38F7FFFFFFFFFFFD39A46FFD5A0 + 51FFD6A154FFD6A154FFD6A154FFD6A154FFD6A154FFD6A154FFD6A154FFD6A1 + 54FFD6A154FFD5A051FFD39A46FFFFFFFFFFA89586FF0000001600000016AA97 + 87FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA39081FFFFFFFFFFBCA0 + 81FFBEA385FFBEA386FFBFA486FFC2A788FFA39080FFFFFFFFFFBE7E25FFC083 + 2FFFC18531FFC18531FFC18531FFC18531FFC18531FFC18531FFC18531FFC185 + 31FFC18531FFC0832FFFBE7E25FFFFFFFFFFA89586FF0000001500000016A895 + 86FFFFFFFFFFC98B34FFCE9440FFD39A47FFD99F47FFA39186FFFFFFFFFFF6FB + FFFFF8FDFFFFF8FEFFFFF9FEFFFFFCFFFFFFA39081FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA9787FF0000000D00000016A895 + 86FFFFFFFFFFCA903DFFD09B4DFFD59F53FFDBA351FFA4948AFFFFFFFFFFEBB1 + 5CFFECB565FFEDB565FFEDB35FFFF0F6FEFFBF9F77FFA4958BFFA4958BFFA495 + 8BFFA4958BFFA4958BFFA4948BFFA39388FFA39185FFA39388FFA4948BFFA494 + 8AFFA39287FFA39182FFA79383FFAA9787FF776A60B30000000400000016A895 + 86FFFFFFFFFFCA923EFFD19C51FFD5A155FFDAA352FFA4958CFFFFFFFFFFDCA3 + 51FFDEA95CFFDEA95DFFDDA553FFF3FDFFFFE0A652FFE2AB5BFFE3AD5CFFE3AD + 5CFFE3AD5CFFE3AD5CFFE3AB5BFFE2A853FFF7FFFFFFE2A853FFE3AB5BFFE3AB + 5AFFE1A751FFFFFFFFFFA99687FF00000016000000000000000000000016A895 + 86FFFFFFFFFFCA913DFFD09C51FFD5A156FFD9A250FFA5958CFFFFFFFFFFCE94 + 3FFFD19B4CFFD19B4DFFCF9642FFF1FBFFFFD09642FFD29C4CFFD29D4FFFD29D + 4FFFD29D4FFFD29D4FFFD29C4CFFD09642FFF2FBFFFFD09642FFD29C4CFFD29B + 4CFFCF9540FFFFFFFFFFA99686FF00000016000000000000000000000016A896 + 86FFFFFFFFFFC98E3CFFCF9A4FFFD5A157FFD9A453FFA5958BFFFFFFFFFFBE7F + 24FFC1842FFFC2852FFFC08027FFF1F6FFFFC08027FFC2852FFFC28631FFC286 + 31FFC28631FFC28631FFC2852FFFC08027FFF1F6FFFFC08027FFC2852FFFC184 + 2FFFBE7F24FFFFFFFFFFA89586FF00000016000000000000000000000016A896 + 86FFFFFFFFFFC78D3BFFCE994DFFD39F54FFD8A354FFA5948AFFFFFFFFFFF4F0 + F7FFF9F3FEFFFAF4FEFFF9F2FBFFF9F0F7FFF9F2FBFFFAF4FEFFFAF4FFFFFAF4 + FFFFFAF4FFFFFAF4FFFFFAF4FEFFF9F2FBFFF9F0F7FFF9F2FBFFFAF4FEFFF9F3 + FEFFF4F0F7FFFFFFFFFFA99585FF00000016000000000000000000000016A896 + 86FFFFFFFFFFC68C38FFCD974BFFD29D52FFD7A051FFA6958BFFFFFFFFFF42B9 + 94FF4BBD9AFF4DBE9BFF4DBD9AFF4DBD9AFF4DBD9AFF4DBE9BFF4DBE9BFF4DBE + 9BFF4DBE9BFF4DBE9BFF4DBE9BFF4DBD9AFF4DBD9AFF4DBD9AFF4DBE9BFF4BBD + 9AFF42B994FFFFFFFFFFAA9585FF00000016000000000000000000000016A896 + 86FFFFFFFFFFC58B37FFCB9649FFD09B4FFFD59E4EFFA6958CFFFFFFFFFF34A4 + 6DFF3DA872FF3FA874FF3FA874FF3FA874FF3FA874FF3FA874FF3FA874FF3FA8 + 74FF3FA874FF3FA874FF3FA874FF3FA874FF3FA874FF3FA874FF3FA874FF3DA8 + 72FF34A46DFFFFFFFFFFAA9585FF00000015000000000000000000000016A896 + 86FFFFFFFFFFC48A35FFCA9447FFCD994DFFD49C4DFFA7978FFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFAB9787FF0000000D000000000000000000000016A896 + 86FFFFFFFFFFC28834FFC89345FFCC974AFFD09B4CFFB89A79FFA7978FFFA695 + 8BFFA7958AFFA7948AFFA6948AFFA6948AFFA69489FFA69389FFA69389FFA693 + 89FFA69389FFA69389FFA69389FFA69389FFA79489FFA79389FFA69286FFA690 + 81FFA89382FFAA9687FF776A60B300000004000000000000000000000016A996 + 86FFFFFFFFFFC08532FFC79143FFCA9549FFCD984CFFD19B4DFFD39E4FFFD7A1 + 52FFD9A357FFDAA75AFFDBA95DFFDEAA60FFDDAC63FFDEAD65FFDEAE66FFDEAE + 67FFDEAE68FFDDAE69FFDCAE69FFDBAD69FFDAAC67FFD7A964FFD3A258FFFFFF + FFFFA99687FF000000160000000000000000000000000000000000000016A996 + 86FFFFFFFFFFBF8330FFC58F41FFC89347FFCA954AFFCD994EFFCF9C51FFD19F + 55FFD4A158FFD6A45CFFD7A660FFD8A861FFD9A963FFD9AA65FFDAAA68FFD9AB + 69FFD9AB6AFFD9AB6BFFD8AB6BFFD7AB6CFFD6AA6BFFD3A766FFCF9E57FFFFFF + FFFFA99686FF000000160000000000000000000000000000000000000016A996 + 86FFFFFFFFFFBD812DFFC38D3FFFC69145FFC89347FFCB964BFFCD994FFFCE9C + 52FFD19F56FFD2A159FFD3A25CFFD5A45EFFD5A661FFD6A763FFD6A864FFD6A8 + 65FFD6A967FFD5A968FFD5A968FFD4A968FFD3A868FFD1A463FFCC9C57FFFFFF + FFFFA89586FF000000160000000000000000000000000000000000000016A996 + 86FFFFFFFFFFBA7F2AFFC18B3DFFC48F42FFC69145FFC89347FFCA964BFFCC99 + 4FFFCE9B52FFD09E55FFD1A058FFD2A15BFFD3A35DFFD3A45FFFD3A561FFD4A5 + 62FFD3A663FFD3A664FFD3A665FFD2A666FFD2A666FFCFA263FFCA9A53FFFFFF + FFFFA89586FF000000160000000000000000000000000000000000000016A996 + 86FFFFFFFFFFBB7F2AFFBE893BFFC28D40FFC48F42FFC69044FFC89348FFCA96 + 4BFFCC984EFFCD9A52FFCE9C55FFCF9E57FFD0A059FFD1A15CFFD1A25DFFD1A2 + 5FFFD1A360FFD1A361FFD1A362FFD0A362FFCFA363FFCDA05FFFC99952FFFFFF + FFFFA89586FF000000160000000000000000000000000000000000000016A996 + 86FFFFFFFFFFBA7E28FFBE8637FFBE8839FFC18A3CFFC38C3EFFC48E41FFC691 + 45FFC89348FFC9954BFFCB974EFFCC9950FFCD9B53FFCD9C55FFCD9D57FFCE9D + 58FFCE9E59FFCE9E5AFFCD9F5BFFCD9E5CFFCC9E5DFFCB9D5BFFC6954EFFFFFF + FFFFA89586FF000000160000000000000000000000000000000000000015A996 + 86FFFFFFFFFFB7781DFFBA7E28FFBB7F2AFFBA7F2AFFBB802CFFBE822EFFC085 + 32FFC18835FFC38A38FFC48C3CFFC58F3FFFC69041FFC89143FFC89246FFC793 + 47FFC79449FFC7944AFFC8954BFFC8954CFFC6954DFFC6944CFFC48F44FFFFFF + FFFFA89586FF00000015000000000000000000000000000000000000000DAA97 + 87FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFAA9787FF0000000D0000000000000000000000000000000000000004786B + 61B4AA9787FFA99686FFA99686FFA99686FFA99686FFA99686FFA99686FFA996 + 86FFA99686FFA89686FFA89686FFA89686FFA89586FFA89586FFA89586FFA895 + 86FFA89586FFA89586FFA89586FFA89586FFA89586FFA89586FFA99586FFAA97 + 87FF786B61B40000000400000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000005000000100000001600000016000000160000 + 0016000000160000001600000016000000160000001600000016000000160000 + 0016000000160000001600000016000000150000000D00000004000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000010000000310000004200000043000000430000 + 0043000000430000004300000043000000430000004300000043000000430000 + 00430000004300000043000000430000003F0000002B0000000D000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000016A5A5A4F1AFAFADFFAEAEABFFADADABFFADAD + ABFFADADABFFADADABFFADADABFFADADABFFADADABFFADADABFFADADABFFADAD + ABFFADADABFFADADABFFAEAEABFFAFAFADFF7C7C7AC400000015000000040000 + 000D000000150000001600000016000000160000001600000016000000160000 + 0016000000160000001600000027B0B0ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0B0ADFF000000160000000D0000 + 002B0000003F0000004300000043000000430000004300000043000000430000 + 0043000000430000004300000048B1B0ACFFFFFFFFFFFFFFFFFFFEFEFFFFFEFE + FFFFFEFEFFFFFEFEFFFFFEFEFFFFFEFEFFFFFEFEFFFFFEFEFFFFFEFEFFFFFEFE + FFFFFEFEFFFFFEFEFFFFFFFFFFFFFFFFFFFFAEAEABFF00000016000000152C48 + 66C43F6893FF3F6893FF3F6893FF3F6893FF3F6893FF3F6893FF3F6893FF3F68 + 93FF3E6793FF376391FF376391FFB5B2ADFFFFFFFFFFFCFCFCFFFCFCFCFFFCFC + FCFFFCFCFCFFFCFCFCFFFBFCFCFFFCFCFCFFFCFCFCFFFCFCFCFFFCFCFCFFFBFC + FCFFFCFCFCFFFCFCFCFFFCFCFCFFFFFFFFFFADADABFF00000016000000164068 + 93FF5085ACFF4C80AAFF4C80AAFF4C80AAFF4C80AAFF4C80AAFF4C80AAFF4C80 + AAFF4C80AAFF487DA9FF487DA9FFB6B2ADFFFFFFFFFFFDFCFCFFFFFEFEFFFFFE + FEFFFFFEFEFFFEFDFDFFFEFCFCFFFFFEFEFFFFFEFEFFFFFEFEFFFEFDFDFFFEFC + FCFFFFFEFEFFFEFDFDFFFCFBFBFFFFFFFFFFADADABFF00000016000000163F68 + 94FF588AB1FF4C80AAFF4C80AAFF4C80AAFF4C80AAFF4C80AAFF4C80AAFF4C80 + AAFF4C80AAFF487EAAFF487EAAFFB6B2ACFFFFFFFFFFFDFDFDFF818181FFB6B6 + B6FFB5B5B5FFB3B3B3FFFFFFFFFF818181FFB6B6B6FFB5B5B5FFB3B3B3FFFFFF + FFFF808080FFB3B3B3FFFBFCFCFFFFFFFFFFADADABFF00000016000000164069 + 94FF5F8FB5FF4C82ACFF4D82ACFF4D82ACFF4D82ACFF4D82ACFF4D82ACFF4D82 + ACFF4D82ACFF4980ACFF4980ACFFB6B2ACFFFFFFFFFFFCFBFBFFFFFFFFFFFFFF + FFFFFFFEFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE + FEFFFFFFFFFFFFFEFEFFFAF9F9FFFFFFFFFFADADABFF00000016000000163F6A + 94FF6595BAFF4E82AEFF4F83AEFF4F83AEFF4F83AEFF4F83AEFF4F83AEFF4F83 + AEFF4F83AEFF4B81AEFF4B81AEFFB6B2ACFFFFFFFFFFFAFAF9FF818181FFB5B5 + B4FFFDFEFCFF828383FFB6B6B5FFB4B4B3FFFDFEFCFF838383FFB6B6B5FFB5B6 + B4FFB5B5B4FFB2B2B1FFF8F8F7FFFFFFFFFFADADABFF00000016000000163F6A + 95FF6C9ABFFF4E84B0FF5085B0FF5085B0FF5085B0FF5085B0FF5085B0FF5085 + B0FF5085B0FF4C83B0FF4C83B0FFB6B1ABFFFFFFFFFFF8F7F7FFFDFCFCFFFEFC + FCFFFCFBFBFFFDFCFCFFFCFBFCFFFDFCFCFFFDFBFCFFFEFCFDFFFDFCFCFFFCFB + FBFFFDFCFCFFFBFAFAFFF7F6F6FFFFFFFFFFADADABFF00000016000000163F6A + 95FF739FC3FF4F86B1FF5187B2FF5187B2FF5187B2FF5187B2FF5187B2FF5187 + B2FF5187B2FF4D85B2FF4D85B2FFB6B1ABFFFFFFFFFFF6F7F5FF838384FFB6B6 + B5FFB4B4B3FFB3B3B2FFFAFAF9FF838484FFB5B5B4FFB5B5B4FFB3B3B2FFFAFA + F9FF828282FFB3B3B2FFF5F5F4FFFFFFFFFFADADABFF00000016000000163E6A + 96FF7AA5C8FF4F86B3FF5188B4FF5288B4FF5288B4FF5288B4FF5288B4FF5288 + B4FF5288B4FF4E86B4FF4E86B4FFB6B1ABFFFFFFFFFFF5F4F2FFFAF9F8FFFAF9 + F8FFF9F8F7FFF9F8F7FFF9F8F7FFFAF9F7FFF9F8F7FFFAF9F8FFFAF9F7FFF9F8 + F7FFFAF9F8FFF8F7F5FFF3F2F1FFFFFFFFFFADADABFF00000016000000163F6B + 95FF81AACCFF5188B5FF548AB6FF548AB6FF548AB6FF548AB6FF548AB6FF548A + B6FF548AB6FF5088B6FF5088B6FFB5B1ABFFFFFFFFFFF3F2F2FF828283FFB3B3 + B3FFF7F6F6FF848484FFB3B3B3FFB1B2B2FFF7F6F6FF848585FFB4B4B4FFB3B3 + B3FFB3B3B3FFB0B0B0FFF1F0F0FFFFFFFFFFADADABFF00000016000000163F6C + 96FF87B0CFFF528AB7FF548CB8FF558CB8FF558CB8FF558CB8FF558CB8FF558C + B8FF558CB8FF518AB8FF518AB8FFB5B1ABFFFFFFFFFFF1F0EFFFF7F6F4FFF7F6 + F5FFF6F5F3FFF6F5F4FFF6F5F4FFF6F5F4FFF6F5F4FFF7F6F5FFF7F5F4FFF6F5 + F4FFF7F6F5FFF5F3F2FFF0EFEEFFFFFFFFFFADADABFF00000016000000163E6C + 97FF8EB4D4FF528BB9FF558DBAFF568DBAFF568DBAFF568DBAFF568DBAFF568D + BAFF568DBAFF528BBAFF528BBAFFB5B1ABFFFFFFFFFFEFEEEEFF858585FFB3B3 + B3FFB2B2B2FFB0B0B1FFF4F2F3FF858585FFB3B3B3FFB2B2B2FFB0B1B1FFF4F3 + F3FF848484FFB0B0B1FFEEEDEDFFFFFFFFFFADADABFF00000016000000163F6C + 98FF94BAD8FF538DBBFF568FBCFF578FBCFF578FBCFF578FBCFF578FBCFF578F + BCFF578FBCFF538DBCFF538DBCFFB5B1ABFFFFFFFFFFEDEDECFFF2F2F1FFF3F3 + F2FFF2F2F1FFF0F0EFFFF2F2F0FFF3F3F2FFF3F3F2FFF2F2F1FFF0F0EFFFF2F2 + F1FFF3F3F2FFF0F0EFFFECECEAFFFFFFFFFFADADABFF00000016000000163E6D + 98FF9ABDDBFF538DBEFF578FBFFF5890BFFF5890BFFF5890BFFF5890BFFF5890 + BFFF5890BFFF548EC0FF548EC0FFB5B1AAFFFFFFFFFFECEBEAFF858586FFB2B2 + B1FFB0B0AFFFF0EFEEFF868686FFB2B3B1FFB1B1B0FFB0B0AFFFF0EFEEFF8485 + 85FFB2B2B1FFAFAFAEFFEBEAE8FFFFFFFFFFADADABFF00000016000000163F6E + 98FFA1C3DEFF558FC0FF5991C1FF5A92C1FF5A92C1FF5A92C1FF5A92C1FF5A92 + C1FF5A92C1FF5790C2FF5790C2FFB5B0AAFFFFFFFFFFE9E9E8FFEFEFEEFFEFEF + EEFFEDEDECFFEEEEEDFFF0F0EFFFEFEFEEFFEEEEEDFFEFEFEEFFEEEFEDFFEFF0 + EEFFEFEFEEFFECEDECFFE8E8E7FFFFFFFFFFAEAEABFF00000016000000163E6E + 99FFA7C8E1FF5691C1FF5A93C3FF5B94C3FF5B94C3FF5B94C3FF5B94C3FF5B94 + C3FF5B94C3FF5893C4FF5893C4FFB5B0AAFFFFFFFFFFE8E7E6FF848484FFB0AF + AFFFECEBEAFF868686FFB1B0B0FFAFAEAEFFECEBEAFF868686FFB1B0B0FFB1B0 + B0FFB0AFAFFFAEADADFFE7E6E4FFFFFFFFFFAEAEABFF00000016000000163E6F + 99FFADCDE5FF5792C3FF5B94C5FF5C95C5FF5C95C5FF5C95C5FF5C95C5FF5C95 + C5FF5C95C5FF5994C6FF5994C6FFB5B0AAFFFFFFFFFFE4E3E3FFE7E6E6FFE7E6 + E6FFE6E5E6FFE8E7E7FFE8E7E7FFE7E6E6FFE6E5E5FFE8E7E7FFE8E7E7FFE7E6 + E6FFE7E6E6FFE6E5E5FFE3E2E2FFFFFFFFFFAEAEABFF00000016000000163F6E + 9AFFB3D0E8FF5794C5FF5C96C7FF5D97C7FF5D97C7FF5D97C7FF5D97C7FF5D97 + C7FF5D97C7FF5A96C8FF5A96C8FFB5B0AAFFFFFFFFFFE0DFDEFFE1E0DFFFE2E1 + DFFFE2E0DFFFE2E1DFFFE2E1DFFFE2E0DFFFE1E0DFFFE2E1DFFFE2E1DFFFE2E1 + DFFFE2E1DFFFE1E0DFFFE0DFDEFFFFFFFFFFAEAEACFF00000015000000163E6F + 9BFFB9D4EBFF5896C7FF5D98C9FF5E99C9FF5E99C9FF5E99C9FF5E99C9FF5E99 + C9FF5E99C9FF5B98CAFF5B98CAFFBAB3ABFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAFAFADFF0000000D000000163E70 + 9AFFBFD9EEFF5A96C9FF5F99CBFF609ACBFF609ACBFF609ACBFF609ACBFF609A + CBFF609ACBFF5F99CBFF5A98CDFF98AAB8FFBAB3AAFFB7B0A9FFB7B0A9FFB7B0 + A9FFB7B0A9FFB6B0A9FFB5AFA8FFB3AFA9FFB3B0ABFFB1AFACFFAFAEACFFAEAE + ABFFAEAEABFFAEAEABFFAEAEACFFB0B0ADFF7C7C7AB400000004000000163E71 + 9BFFC4DCF0FF5A98CBFF609BCDFF619CCDFF619CCDFF619CCDFF609CCEFF609C + CEFF609CCEFF609CCEFF5E9BCFFF5B9ACFFF5799CFFF5698CFFF5698CFFF5798 + CFFF5798CEFF5697CEFF5295CDFFBCD9F1FF2F6899FF00000016000000000000 + 0000000000000000000000000000000000000000000000000000000000163E72 + 9CFFCBE1F3FF5B9ACDFF609DCFFF629ECFFF619ED0FF5E9ED2FF5C9ED3FF5B9E + D4FF5B9ED4FF5B9ED4FF5B9ED4FF5A9DD4FF5A9DD4FF5B9DD4FF5D9ED2FF5F9E + D1FF609ED0FF5F9DCFFF5A9ACEFFC9E0F4FF396F9BFF00000016000000000000 + 0000000000000000000000000000000000000000000000000000000000163F71 + 9DFFD0E6F5FF5B9ACFFF619DD0FF629ED1FF5F9FD4FF8699A8FF9C948DFF9993 + 8EFF99938EFF99938EFF99938EFF99938EFF99938EFF9C948DFF8699A8FF5F9F + D4FF629ED1FF619DD0FF5B9ACFFFD0E6F5FF3E719DFF00000016000000000000 + 0000000000000000000000000000000000000000000000000000000000153F73 + 9DFFD8E8F8FF5A9CD1FF5E9ED2FF5F9FD3FF5B9FD7FF9A938DFFFAF7F5FFE7E4 + E3FFE7E4E3FFE7E4E3FFE7E4E3FFE7E4E3FFE7E4E3FFFAF7F5FF9B938DFF5B9F + D7FF5F9FD3FF5E9ED2FF5A9CD1FFD8E8F8FF3F739DFF00000015000000000000 + 00000000000000000000000000000000000000000000000000000000000D4276 + A0FFAFD2F1FFADD0EEFFAED0EDFFAED1EEFFAAD1F2FF97928EFFE6E4E2FFC8C4 + C3FFC7C4C3FFC7C4C3FFC7C4C3FFC7C4C3FFC8C4C3FFE6E4E2FF98928EFFAAD1 + F2FFAED1EEFFAED0EDFFADD0EEFFAFD2F1FF4276A0FF0000000D000000000000 + 0000000000000000000000000000000000000000000000000000000000043155 + 71B44478A1FF4276A0FF4276A0FF4176A0FF3B74A2FF9B9692FFD7D3D2FFD2D0 + CFFFCFCDCCFFCECCCAFFCDCBCAFFCDCBCAFFCFCDCCFFD2CFCDFF9B9692FF3B74 + A2FF4176A0FF4276A0FF4276A0FF4478A1FF315571B400000004000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000000D9090 + 90FFE5E5E5FFE3E3E3FFE3E3E3FFE5E5E5FF909091FF0000000D000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000046767 + 67B3959595FF949494FF949494FF959595FF676767B300000004000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000} + end + item + Image.Data = { + 36100000424D3610000000000000360000002800000020000000200000000100 + 2000000000000010000000000000000000000000000000000000000000000000 + 0000000000040000000D00000015000000160000001600000016000000160000 + 00160000001600000016000000150000000D0000000400000000000000000000 + 00040000000D0000001500000016000000160000001600000016000000160000 + 001600000016000000150000000D000000040000000000000000000000000000 + 00000000000D0000002B0000003F000000430000004300000043000000430000 + 004300000043000000430000003F0000002B0000000D00000000000000000000 + 000D0000002B0000003F00000043000000430000004300000043000000430000 + 0043000000430000003F0000002B0000000D0000000000000000000000000000 + 000000000015555553C4797675FF747371FF716F6DFF6F6C6BFF6C6968FF6A68 + 66FF696665FF676463FF646160FF444141C40000001500000000000000000000 + 0015555553C4797675FF747371FF716F6DFF6F6C6BFF6C6968FF6A6866FF6966 + 65FF676463FF646160FF444141C4000000150000000000000000000000000000 + 0000000000167B7977FF7F7B7AFF928F8EFFA29F9DFF989593FF8B8987FF7F7C + 7BFF72706FFF656461FF595655FF625F5EFF0000001600000000000000000000 + 00167B7977FF7F7B7AFF928F8EFFA29F9DFF989593FF8B8987FF7F7C7BFF7270 + 6FFF656461FF595655FF625F5EFF000000160000000000000000000000000000 + 000000000016797775FF7A7776FF8B8887FF9A9795FF908D8BFF83817FFF7875 + 74FF6C6A69FF615F5CFF565251FF605D5CFF0000001600000000000000000000 + 0016797775FF7A7776FF8B8887FF9A9795FF908D8BFF83817FFF787574FF6C6A + 69FF615F5CFF565251FF605D5CFF000000160000000000000000000000000000 + 000000000016777573FFDBDAD8FFE2E2E1FFEBECEBFFE3E3E2FFDAD9D8FFD0CF + CFFFC8C7C6FFBEBEBCFFB7B5B5FF5F5C5BFF0000001600000000000000000000 + 0016777573FFDBDAD8FFE2E2E1FFEBECEBFFE3E3E2FFDAD9D8FFD0CFCFFFC8C7 + C6FFBEBEBCFFB7B5B5FF5F5C5BFF000000160000000000000000000000000000 + 000000000016787674FF7D7B7AFF898786FF949291FF8A8988FF7D7C7BFF7370 + 6FFF676665FF5B5A59FF4F4E4DFF605D5CFF0000001600000000000000000000 + 0016787674FF7D7B7AFF898786FF949291FF8A8988FF7D7C7BFF73706FFF6766 + 65FF5B5A59FF4F4E4DFF605D5CFF000000160000000000000000000000000000 + 000000000016787674FFB1AFADFFC1C0BEFFD0CFCDFFC3C2C0FFB3B2B0FFA3A1 + A0FF939190FF848281FF747271FF605D5CFF0000002700000016000000160000 + 0027797775FFB1AFADFFC1C0BEFFD0CFCDFFC3C2C0FFB3B2B0FFA3A1A0FF9391 + 90FF848281FF747271FF605D5CFF000000160000000000000000000000000000 + 000000000016777573FFB0AEACFF888685FFCFCECCFF888787FFB2B1AFFF7371 + 70FF939190FF5C5B5AFF737170FF5E5B5AFF0000004800000043000000430000 + 0048797775FFB0AEACFF888685FFCFCECCFF888787FFB2B1AFFF737170FF9391 + 90FF5C5B5AFF737170FF605D5CFF000000160000000000000000000000000000 + 000000000016777573FFAFADABFFBEBDBBFFCDCCCAFFC0BFBDFFB0AFADFFA19F + 9EFF92908FFF82807FFF72706FFF5C5958FF797675FF6D6B69FF666262FF5D5A + 59FF787674FFAFADABFFBEBDBBFFCDCCCAFFC0BFBDFFB0AFADFFA19F9EFF9290 + 8FFF82807FFF72706FFF605D5CFF000000160000000000000000000000000000 + 000000000016777573FFAEACAAFFBDBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D + 9CFF908E8DFF817F7EFF716F6EFF595655FFBEBCBAFFCDCBCAFFA3A1A0FF7A78 + 77FF767472FFAEACAAFFBDBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D9CFF908E + 8DFF817F7EFF72706FFF605D5CFF000000160000000000000000000000000000 + 000000000016777573FFAEACAAFFBCBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D + 9CFF908E8DFF817F7EFF716F6EFF585554FFE3E3E1FFE9EAE9FFD1D0CFFFB8B8 + B7FF757371FFAEACAAFFBCBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D9CFF908E + 8DFF817F7EFF72706FFF605D5CFF000000160000000000000000000000000000 + 000000000016777573FFAEACAAFFBCBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D + 9CFF908E8DFF817F7EFF716F6EFF5B5857FF787574FF6B6966FF63605FFF5B58 + 57FF777573FFAEACAAFFBCBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D9CFF908E + 8DFF817F7EFF72706FFF605D5CFF000000160000000000000000000000000000 + 000000000016767472FFADABA9FFBBBBB9FFCBCAC8FFBEBDBBFFAEADABFF9F9D + 9CFF908E8DFF817F7EFF72706FFF5E5B5AFF0000001600000000000000000000 + 0016787674FFAEACAAFFBCBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D9CFF908E + 8DFF807E7DFF706E6DFF5F5C5BFF000000160000000000000000000000000000 + 000000000015767371FFCCCDCCFFBAB9B7FFCAC9C7FFBEBDBBFFAEADABFF9F9D + 9CFF908E8DFF817F7EFF72706FFF5F5C5BFF0000001600000000000000000000 + 0016777573FFAEACAAFFBCBCBAFFCBCAC8FFBEBDBBFFAEADABFF9F9D9CFF8F8D + 8CFF7E7C7BFF979696FF5E5B5AFF000000150000000000000000000000000000 + 00000000000D767472FFCAC9C9FFCECECDFFCAC9C7FFBDBCBAFFAEADABFF9E9C + 9BFF8F8D8CFF807E7DFF716F6EFF605D5CFF0000001600000000000000000000 + 0016777573FFAEACAAFFBCBBB9FFCBCAC8FFBDBCBAFFAEADABFF9E9C9BFF8D8B + 8AFF9F9E9DFF959393FF5E5B5AFF0000000D0000000000000000000000000000 + 000000000004514F4EB3918E8CFFD0CFCFFFD3D4D3FFBEBDBBFFADACA9FF9D9B + 9AFF8D8B8AFF7E7C7BFF6F6D6CFF5F5C5BFF0000001400000000000000000000 + 0014767472FFADABA9FFBBBAB8FFCAC9C7FFBDBCBAFFADACA9FF9D9B9AFFA9A8 + A8FF9F9E9DFF676564FF413E3EB3000000040000000000000000000000000000 + 000000000000000000034C4B4AAE7F7D7AFFC7C6C5FFD2D1D2FFD9D8D7FFCDCD + CDFFC5C4C3FFBBBBBAFFB5B3B3FF5E5B5AFF0000001200000000000000000000 + 0012777573FFD9D8D6FFE1E0DFFFEAEAE9FFE2E2E1FFD9D9D8FFB7B6B5FFA4A1 + A2FF676564FF403E3DAE00000003000000000000000000000000000000000000 + 00000000000000000000000000034A4847AE6B6967FF646160FF5E5B5AFF5C5A + 58FF5C5958FF5C5958FF5D5A59FF3B3838B60000002500000016000000160000 + 00254A4947B66F6C6BFF676563FF63615FFF635F5FFF63605FFF64625FFF6562 + 61FF413F3EAE0000000300000000000000000000000000000000000000000000 + 000000000000000000000000000000000016787674FFB5B3B1FFD0CFCDFFC2C1 + BFFFA9A7A6FF908E8DFF767473FF5F5C5BFF0000004800000043000000430000 + 0048797775FFB5B3B1FFCFCECCFFC1C0BEFFA8A6A5FF8F8D8CFF757373FF605D + 5CFF000000160000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000016777573FFB2B0AEFFCBCAC8FFBDBC + BAFFA5A3A2FF8E8C8BFF757372FF5D5A59FF797675FF6D6B69FF666262FF5D5A + 59FF787674FFB2B0AEFFCBCAC8FFBDBCBAFFA5A3A2FF8D8B8AFF767473FF615D + 5CFF000000160000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000016767472FFB1AFADFFCAC9C7FFBCBB + B9FFA4A2A1FF8D8B8AFF747271FF595655FFBEBCBAFFCDCBCAFFA3A1A0FF7A78 + 77FF767472FFB1AFADFFCAC9C7FFBCBBB9FFA4A2A1FF8D8B8AFF757372FF615D + 5CFF000000160000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000016767472FFB1AFADFFCAC9C7FFBCBB + B9FFA4A2A1FF8C8A89FF737170FF585554FFE3E3E0FFE9EAE9FFD1D0CFFFB8B8 + B7FF747270FFB1AFACFFCAC9C7FFBCBBB9FFA4A2A1FF8C8A89FF757371FF605D + 5CFF000000160000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000014767472FFB0AEACFFC9C8C6FFBAB9 + B7FFA2A09FFF8A8887FF72706FFF5B5857FF787574FF6B6966FF63605FFF5B58 + 57FF767472FFB0AEACFFC9C8C6FFBAB9B7FFA2A09FFF8A8887FF737170FF605C + 5BFF000000140000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000D777573FFDCDBD9FFE9EAE8FFE1E0 + E0FFD2D0D1FFC4C3C2FFB6B5B6FF5D5A59FF0000000D00000000000000000000 + 000D787674FFDCDBD9FFE9EAE8FFE1E0E0FFD2D0D1FFC4C3C2FFB6B6B6FF5F5C + 5AFF0000000D0000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000044F4E4DB2716D6DFF676563FF615F + 5DFF605D5CFF615D5CFF605E5CFF3F3D3CB10000000300000000000000000000 + 00034F4E4DB1716E6DFF676563FF615F5DFF605D5CFF615D5CFF605F5CFF403E + 3DB2000000040000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000040000001E777473FFBCBAB8FFCECC + CBFFA5A3A2FF7E7C7BFF605C5CFF0000001E0000000400000000000000000000 + 00040000001E777473FFBCBAB8FFCECCCBFFA5A3A2FF7E7C7BFF605C5CFF0000 + 001E000000040000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000D00000030787574FFBEBCBAFFCFCD + CCFFA6A4A3FF807E7DFF605D5CFF000000300000000D00000000000000000000 + 000D00000030787574FFBEBCBAFFCFCDCCFFA6A4A3FF807E7DFF605D5CFF0000 + 00300000000D0000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000014514F4EBD757271FF6C6A68FF6765 + 63FF666362FF666362FF656361FF413F3FBD0000001400000000000000000000 + 0014514F4EBD757271FF6C6A68FF676563FF666362FF666362FF656361FF413F + 3FBD000000140000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000167A7876FF807D7CFF9B9896FF9490 + 8EFF7E7C7AFF6C6A67FF595756FF615E5DFF0000001600000000000000000000 + 00167A7876FF807D7CFF9B9896FF94908EFF7E7C7AFF6C6A67FF595756FF615E + 5DFF000000160000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000011797775FFBEBCBBFFCDCCCBFFC7C7 + C6FFBDBABAFFB1AFAFFFA7A5A6FF605D5CFF0000001100000000000000000000 + 0011797775FFBEBCBBFFCDCCCBFFC7C7C6FFBDBABAFFB1AFAFFFA7A5A6FF605D + 5CFF000000110000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000067B7977FF757271FF6F6D6BFF6A68 + 66FF686564FF656261FF63615EFF615E5DFF0000000600000000000000000000 + 00067B7977FF757271FF6F6D6BFF6A6866FF686564FF656261FF63615EFF615E + 5DFF000000060000000000000000000000000000000000000000} + end> + end + object DockingManager: TdxDockingManager + Color = clBtnFace + DefaultHorizContainerSiteProperties.CustomCaptionButtons.Buttons = <> + DefaultHorizContainerSiteProperties.Dockable = True + DefaultHorizContainerSiteProperties.ImageIndex = -1 + DefaultVertContainerSiteProperties.CustomCaptionButtons.Buttons = <> + DefaultVertContainerSiteProperties.Dockable = True + DefaultVertContainerSiteProperties.ImageIndex = -1 + DefaultTabContainerSiteProperties.CustomCaptionButtons.Buttons = <> + DefaultTabContainerSiteProperties.Dockable = True + DefaultTabContainerSiteProperties.ImageIndex = -1 + DefaultTabContainerSiteProperties.TabsProperties.CustomButtons.Buttons = <> + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + Images = imgMisc + LookAndFeel.NativeStyle = False + Left = 624 + Top = 176 + PixelsPerInch = 96 + end + object imgMisc: TcxImageList + FormatVersion = 1 + DesignInfo = 15729048 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000240000 + 0033000000330000003300000033000000330000003300000033000000330000 + 00330000003300000033000000330000003300000033000000247E5606C1B67E + 0EFFB57C09FFB57C09FFB57C09FFB57B08FFB47B08FFB57B08FFB57B08FFB47B + 08FFB57B08FFB57B08FFB57B08FFB57C09FFB67E0EFF7E5606C1B67E0EFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB67E0EFFB57C09FFFFFF + FFFFA09D9EFF8F8F8FFF939291FFD2D1D2FFFFFFFFFFC1C2C3FFC1C2C4FFFFFF + FFFFC1C2C4FFC3C4C6FFC3C4C6FFC2C4C7FFFFFFFFFFB57B09FFB57B08FFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB47B08FFB57B08FFFFFF + FFFF999696FF8B8886FF8F8C8AFFCAC9C8FFFCFCFBFFBBBBBBFFBBBBBBFFFCFC + FBFFBBBBBBFFBDBDBDFFBDBDBDFFBBBCBEFFFFFFFFFFB57B08FFB57B08FFFFFF + FFFFFCFDFEFFFFFFFFFFFFFFFEFFFAFAF9FFF7F7F6FFF8F8F7FFF8F8F7FFF7F7 + F6FFF8F9F7FFFAFAF9FFFAFAF9FFF8F9F9FFFFFFFFFFB57B08FFB57B08FFFFFF + FFFF92908FFF868381FF888684FFC4C1BFFFF3F2F1FFB3B2B2FFB3B2B2FFF4F2 + F1FFB3B2B2FFB5B4B4FFB5B4B4FFB2B2B4FFFFFFFFFFB57B08FFB57B08FFFFFF + FFFFF2F1F2FFF6F5F5FFF5F4F4FFF0F0EFFFEEEDECFFEFEEEDFFEFEEEDFFEEED + ECFFEFEEEDFFF1F0EFFFF1F0EFFFEEEEEEFFFFFFFFFFB57B08FFB57C09FFFFFF + FFFF8B8988FF807E7CFF83817FFFBABAB7FFE9E8E7FFACABAAFFADACABFFE9E8 + E7FFADACABFFAFAEADFFAEADACFFABABABFFFFFFFFFFB57B08FFB57B09FFFFFF + FFFFE6E6E5FFEBEAE8FFEAE9E7FFE5E4E2FFE2E1DFFFE4E3E1FFE4E3E1FFE3E2 + DFFFE4E3E1FFE6E5E2FFE5E4E2FFE2E2E1FFFFFFFFFFB57B08FFB57C09FFFFFF + FFFF807E7DFF767372FF797776FFAEADACFFDADAD9FFA1A09FFFA1A09FFFDADA + DAFFA1A1A0FFA2A2A1FFA2A2A1FF9F9FA0FFFFFFFFFFB57B09FFB57C0BFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB57C0BFFB67F10FFF5DD + B7FFDC9D37FFDC9E39FFDC9F3AFFDB9E39FFDB9E39FFDB9E39FFDB9E39FFDB9E + 39FFDB9E39FFDC9E39FFDB9E39FFDB9D37FFF5DDB7FFB67F10FFB88216FFEFD0 + 9EFFECCD99FFECCD99FFECCD9AFFECCD9AFFECCD9AFFECCD9AFFECCD9AFFECCD + 9AFFECCD9AFFECCD9AFFECCD99FFECCD99FFEFD09EFFB88216FF7E5606B2B882 + 17FFB78115FFB68114FFB68115FFB68115FFB68115FFB68115FFB68115FFB681 + 15FFB68115FFB68115FFB68114FFB78115FFB88217FF7E5606B2} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0023000000330000003300000033000000330000003300000033000000330000 + 003300000033000000330000002600000003000000000000000000000000695B + 4BC096816AFF958069FF958069FF95816AFF95846FFF988169FFA76730FFAF59 + 10FFAD5911FFAE5A13FF824411CA0905013D0000000300000000000000009884 + 6CFFD4CAB7FFCFC4AFFFCFC4AFFFCFC7B3FFCCBDA5FFB05B12FFCD8B47FFE6B5 + 75FFE4B271FFE6B676FFCD8E4CFFAE5B15FF0905013100000000000000009883 + 6DFFD7CCBAFFDDC9B6FFCCC2ADFFDECFC0FFB7773CFFCA833EFFDEAA66FFDAA2 + 5BFFFFFFFFFFDAA25BFFDEAA68FFCB8846FF834511CA00000000000000009A85 + 6DFFDAD0C0FFCEC1ADFFCEC3AFFFD0C9BAFFAB4F02FFDAA05CFFD69C58FFD394 + 4CFFFFFFFFFFD3944CFFD69C58FFDBA360FFAF5B15FF00000000000000009A85 + 6FFFDFD7C6FFD1C5B1FFD2C6B3FFD3CDBFFFAB5003FFDDAB73FFD19149FFCF8C + 41FFFFFFFFFFCF8C41FFD19149FFDEAD76FFAE5A13FF00000000000000009C87 + 71FFE4DBCEFFB9A189FFBAA38CFFBCA996FFAA4E00FFEACBA7FFCB8639FFCA84 + 36FFC88130FFCA8436FFCB873AFFEACEABFFAE5A12FF00000000000000009D88 + 71FFE6DFD3FFF0DFCFFFF0E0D2FFF1E5DAFFC27F46FFD09A67FFE0B78AFFC882 + 36FFFFFFFFFFC88336FFE1B98CFFD2A070FF82450FBE00000000000000009F8A + 73FFEBE5D9FFB79F86FFB7A189FFB9A48DFFBA9F84FFAB5308FFCD9661FFE9CC + ACFFE9CBAAFFEACEB0FFCF9C6BFFAE5911FF0905010D0000000000000000A08A + 75FFEEE8DFFFEEDDCEFFEFDFD0FFF0E0D2FFF0E3D6FFEAD5C3FFC07F47FFAC50 + 02FFAD550AFFAD580FFF82430EBD0905010D000000000000000000000000A28C + 75FFF2ECE4FFB69E85FFB7A087FFB7A088FFB7A189FFB7A18AFFF3F2EDFFA094 + 83FF00000000000000000000000000000000000000000000000000000000A28D + 77FFF5F0E8FFECDACAFFECDBCCFFEDDCCCFFECDBCCFFECDBCBFFF5F1EAFFA290 + 7CFF00000000000000000000000000000000000000000000000000000000A58F + 79FFF8F3ECFFDACAB6FFD9CAB6FFDACAB7FFD9CAB6FFDACAB6FFF8F3ECFFA48F + 7AFF00000000000000000000000000000000000000000000000000000000A691 + 7BFFFCF9F3FFF8F3EFFFF8F2EEFFF8F2EEFFF8F2EEFFF8F3EFFFFCF9F3FFA691 + 7BFF000000000000000000000000000000000000000000000000000000007366 + 56ACC5B5A0FFD9CBB7FFD8CAB6FFD8CAB5FFD8CAB6FFD9CBB7FFC5B5A0FF7366 + 56AC000000000000000000000000000000000000000000000000000000000000 + 0000746758ACAC9782FFAB9781FFAB9781FFAB9781FFAC9782FF746758AC0000 + 0000000000000000000000000000000000000000000000000000} + end> + end + object popupEditor: TdxRibbonPopupMenu + BarManager = BarManager + ItemLinks = < + item + Visible = True + ItemName = 'dxBarSeparator1' + end + item + Visible = True + ItemName = 'lbCreateDefinition' + end + item + Visible = True + ItemName = 'dxBarSeparator2' + end + item + Visible = True + ItemName = 'bbDuplicateDefinition' + end + item + Visible = True + ItemName = 'bbDeleteDefinition' + end + item + Visible = True + ItemName = 'dxBarSeparator4' + end + item + Visible = True + ItemName = 'bbExpandLeaf' + end + item + Visible = True + ItemName = 'bbCollapseLeaf' + end + item + Visible = True + ItemName = 'dxBarSeparator3' + end + item + Visible = True + ItemName = 'lbClipboardPaste' + end + item + Visible = True + ItemName = 'bbClipboardCopy' + end + item + Visible = True + ItemName = 'bbClipboardCut' + end> + Ribbon = Ribbon + UseOwnFont = False + Left = 480 + Top = 240 + end + object imgTreeView: TcxImageList + FormatVersion = 1 + DesignInfo = 15729264 + ImageInfo = < + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000220000 + 0033000000330000003300000033000000330000002200000000000000000000 + 000000000000000000000000000000000000000000000000002A77736BBCB0AA + 9EFFAFA99DFFAEA89CFFAEA99CFFB0AA9EFF77736BBC0000002B000000000000 + 000B0000000A0000000A0000000A0000000A000000079B8F81DBBAB3A6FFD3CA + C1FFE4DAD2FFEFE4DDFFE8DDD6FFD5CDC4FFB9B4A7FF959085DD0000002F0F21 + 2D5F0E1F2A5C0E1F2A5C0E1F2A5C0D1D285908161F51BCAD9AFFCBC3B8FFD4CC + C4FFE1D6CEFFEBE1D9FFE4DAD2FFD7D0C7FFCDC6BCFFB1AB9FFF4190C5F14398 + D2FF4094D0FF3E92CFFF3E92CEFF3D92D0FF3592D5FFBAAA98FFCAC2B7FFD3CB + C2FFDFD5CDFFEAE0D8FFE3D8D0FFD6CEC5FFCCC5BBFFB0AA9EFF4499D2FF3F94 + D0FFABFBFFFF9BF3FFFF92F1FFFF92F1FFFF8CF4FFFFB6A695FFC8BFB4FFD1C8 + C0FFDED3CBFFE8DED6FFE1D6CEFFD4CCC3FFCAC3B9FFB0AA9EFF4397D1FF56AC + DDFF8EDAF5FFA2EDFFFF82E5FEFF83E6FFFF7EEAFFFFB4A493FFE5DDD5FFF8F4 + EEFFFDF9F2FFF9F5EDFFFDF8F2FFF9F4EEFFE6DFD8FFAEA99CFF4296D1FF71C4 + EAFF6CBCE6FFBBF2FFFF75DEFDFF75DFFEFF73E3FFFFB4A392FFF0E5DCFFDDD3 + C8FFD8CFC5FFD8CFC5FFD8CFC5FFDDD3C8FFEFE7DEFFB0A99CFF4095D0FF90DD + F8FF44A0D8FFDDFCFFFFDAFAFFFFDAFAFFFFD9FDFFFFA5B3AEFFD1C2B4FFEBE0 + D5FFEDE4DAFFEDE4DAFFEDE4DAFFEAE0D5FFD0C4B6FF928779D13E93CFFFB2F6 + FFFF51ACDEFF358ACAFF358ACAFF358ACBFF348BCDFF5ABDEAFF94BDC4FFAEA6 + 99FFAFA393FFAEA393FFAEA293FFB1A89AFFAFC0C2FF2C6A94C43D92CFFFB8F3 + FFFF77DFFEFF7BE0FEFF7CE1FEFF7CE1FFFF7DE2FFFF50ACE0FF51BBEDFFD3FB + FFFFCFFAFFFFCEF9FFFFCEF9FFFFD0FAFFFFD7FEFFFF3B95D3FF3C92CFFFC0F3 + FFFF70D9FBFF73DAFBFF74DAFBFF74DAFBFF74DBFCFF76DEFDFF4FAADDFF358C + CBFF338CCCFF328CCCFF328DCDFF3690CFFF3C94D0FF2D688FAE3B92CFFFCAF6 + FFFF69D5F9FF6CD5F9FF6AD4F9FF69D4F9FF69D5F9FF6AD6FAFF6BD8FBFF6BD9 + FCFF6BDAFDFF69DAFDFFDAFDFFFF3C93D0FF0A171F2F000000003B92CFFFD5F7 + FFFF60D1F9FF61D0F8FFB4EBFDFFD9F6FFFFDAF8FFFFDAF8FFFFDAF9FFFFDBF9 + FFFFDAF9FFFFDAFAFFFFDFFEFFFF3D94D0FF0E202C35000000003D94D0FFDCFC + FFFFD8F7FFFFD8F7FFFFDBFAFFFF358ECDFF3991CEFF3A92CFFF3A92CFFF3A92 + CFFF3A92CFFF3A92CFFF3D94D0FF3D8BC1EA00000000000000003F8EC5EF3D94 + D0FF3A92CFFF3A92CFFF3D94D0FF3A87BBE40B1A232B0D1E29320E1E2A330E1E + 2A330E1E2A330E1F2A330F212E37000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 0000000000000000002200000033000000330000003300000033000000330000 + 0033000000330000002300000000000000000000000000000000000000000000 + 000A000000258C6839BCCE9956FFCE964EFFAFB4BAFFB1B1B2FFB1B1B1FFB1B1 + B1FFB2B2B2FF7C7C7CBF000000000000000000000000000000000000001F2323 + 205C7E7C77C5D09851FFEDDEBAFFE6CF9FFFFAFCFFFFFAF9FAFFFAF9F9FFFAF9 + F9FFFFFFFFFFB2B2B2FF000000330000002300000000000000006B6760ADB4AF + A4FFC4C1BBFFCE964EFFEBD9B5FFE1C38CFFF0F3F7FFEFF0F0FFEFEFEEFFF0F0 + EFFFF9F9F8FFB0B0B0FFB3B3B3FF7C7C7CBE0000000000000000B1AB9FFFC8C2 + B7FFD0CBC8FFCD954DFFECDAB7FFDEBB7EFFEBECF1FFEAE9E9FFEAE9E8FFEAE9 + E8FFF6F6F5FFADADADFFFFFFFFFFB3B3B3FF0000003300000023B0AB9EFFC8C1 + B8FFCEC9C6FFCC944BFFEFDCBBFFDCB36FFFE5E7EBFFE4E4E3FFE4E3E2FFE5E3 + E2FFF5F5F4FFACACACFFFEFEFCFFB0B1B1FFB3B3B3FF7C7C7CBFB0AA9EFFCAC3 + BAFFECEAE7FFCC944AFFF2E2C3FFF2DEBBFFF6F8FCFFF6F5F5FFF5F4F4FFF5F4 + F4FFF7F6F6FFACACADFFFAFAF9FFAEAEAEFFFFFFFFFFB3B3B3FF6F6B64ADF9F4 + EEFFD8D4CFFFC59F6EFFD09749FFD59843FFB5B7B2FFB3B3ADFFAFAFACFFADAD + ACFFACADADFFC3C3C2FFF8F8F7FFADADADFFFEFEFDFFB2B2B2FF706C64B4A39A + 8FFFBAB3AAFFCCC5BEFFE7E0D3FF1F39DCFF9DAFFFFF98A9FFFFFFFEF7FFFCF9 + F8FFF9F7F7FFF8F7F7FFF8F7F7FFACACADFFFBFBFAFFB2B2B2FFB1AB9FFFC9C2 + B9FFCFC8BFFFD7CEC6FFE5DBCBFF7179D3FF293BD5FF2D39D8FFC3B6B1FFB9B0 + B1FFB1AEAEFFADADADFFADADADFFC3C3C2FFF9F9F8FFB2B2B2FFB0AB9EFFC7C0 + B6FFCCC4BBFFD3CAC1FFDDD3C7FFEDDFD0FFFFF0E0FF009B5AFF79EBD3FF6EE6 + D0FFFFF9FCFFFBF8F9FFF9F8F8FFF9F7F8FFFAF9F9FFB3B3B3FFAFAA9DFFC8C1 + B7FFE8E3DBFFF5F0E8FFFDF9F2FFFFF9F2FFFFFDF6FF4CBC98FF00955FFF0094 + 5CFFB9AFB3FFB0AFB1FFB1B1B2FFB2B2B2FFB3B3B3FF7D7D7DB0AEA89CFFFBF5 + EFFFEEE7DEFFE0D8CEFFDDD5CCFFDDD5CCFFE2D6CEFFEBD9D2FFF1DBD5FFF1DD + D6FFF6E9E1FFFDF7EEFFAEA799FF000000000000000000000000AFA99DFFE1D8 + CEFFE1D7CEFFDFD6CCFFDFD5CBFFDFD5CBFFDFD6CCFFE1D6CCFFE2D6CDFFE2D7 + CDFFE2D8CEFFE2D8CEFFAFA99CFF0000000000000000000000006A675F99B6B0 + A4FFD6CFC4FFE6DED5FFEFE7DDFFEEE6DCFFEEE6DCFFEEE6DCFFEFE7DDFFE6DE + D5FFD6CFC4FFB6B0A4FF6A675F99000000000000000000000000000000002322 + 2033827D74BCAEA99CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA9 + 9CFF827D74BC2322203300000000000000000000000000000000} + end + item + Image.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000000000000000000000000000000000000000000000000 + 00000000000B0000002200000031000000339F8F96F5A9979CFFA79898FFA49D + 95FFA29F92FFA09F92FF9E9E97FF979A9CFF9498A0FF8C909AF2000000000000 + 00172524215D757169B9AAA498F7AFA99DFF008941FF00E29EFF15EFA7FF2227 + C6FF5E71FFFF7386FFFF1228CBFFFFC963FFFCD086FFBB810BFF000000004F4C + 478DB4ADA2FFC2BBB0FFD0C7BEFFDFD5CDFF008946FF00D49FFF37E8AFFF2A2B + C2FF5A6CFFFF788BFFFF152DCAFFFABC4BFFF4C980FFB78115FF00000000B1AB + 9FFFC7C0B6FFCFC7BEFFD5CCC3FFDBD2C9FF008946FF00C79FFF50E4B9FF2B2C + C1FF4F65FBFF7A8CFFFF172ECBFFF5B033FFF3C67CFFB68217FF00000000B1AB + 9FFFC9C0B8FFCDC5BCFFD4CBC2FFDAD1C8FF008A47FF53EBDDFF69E7C8FF2C2F + C4FF445CF7FF7B8EFCFF172ECCFFF0A218FFF1C479FFB68217FF00000000B0AB + 9EFFC8BFB7FFCBC3BAFFD2C9C0FFD8CFC5FF0F9255FF008B45FF00923AFF3032 + C9FF3B52F3FF7E8FFAFF182ECCFFFBDB7CFFF3C57AFFB88319FF00000000B0AA + 9EFFC9C2B9FFE2DDD4FFF5EFE9FFFFFBF4FFFEFAF3FFFDF9F2FFFDF9F2FF3233 + CBFF2F47EFFF8192F8FF1A30CEFFC68A06FFBB8516FF805C14B0000000006F6B + 64ADF9F4EEFFD7D0C8FFC6BEB6FFBEB5ABFFC2B9AFFFC7BCB4FFC8BEB5FF3135 + CAFF71B4FAFF8798F8FF2035CDFFF9F5EDFF6F6B64AD0000000000000000706C + 64B4A3998FFFB6AEA4FFC8BFB6FFD8CFC7FFE3D9D1FFEADED8FFECE1DAFF676C + CAFF2C3AC1FF2B3AC2FF555FBEFFA49C92FF706C64B40000000000000000B1AB + 9FFFCAC1B9FFCEC7BEFFD5CCC3FFDAD1C8FFE1D7CFFFE8DCD6FFEADFD8FFE2D8 + D1FFDCD2CAFFD6CEC5FFD1C9C0FFCBC4BAFFB1AB9FFF0000000000000000B1AB + 9EFFC8BFB7FFCBC3BAFFD2C8BFFFD8CEC5FFDFD5CDFFE9DED8FFE9DDD6FFE0D6 + CFFFDACFC7FFD3CBC2FFCEC6BDFFC9C2B8FFB0AB9EFF0000000000000000B0AA + 9DFFC7C0B8FFE1DBD2FFF3EDE6FFFDF9F2FFFCF8F1FFFBF7F0FFFBF7F0FFFCF8 + F1FFFDF8F2FFF4EEE6FFE2DBD3FFCAC3B9FFAFAA9DFF0000000000000000AEA8 + 9CFFFBF5EFFFEEE7DEFFE3DCD3FFDDD5CCFFDDD5CBFFDDD4CBFFDDD4CBFFDDD4 + CBFFDDD5CBFFE3DCD3FFEEE7DEFFFBF6EFFFAEA89CFF0000000000000000AFA9 + 9DFFE1D8CEFFE1D8CEFFDFD6CCFFDFD6CBFFDFD5CBFFDFD5CBFFDFD5CBFFDFD5 + CBFFDFD6CBFFDFD6CCFFE1D8CEFFE1D8CEFFAFA99DFF00000000000000004E4C + 4671B6B0A4FFD2CABFFFE2D9D0FFEFE7DDFFEEE6DCFFEEE6DCFFEEE6DCFFEEE6 + DCFFEFE7DDFFE2D9D0FFD2CABFFFB6B0A4FF4E4C467100000000000000000000 + 000025242135747068A8A8A297F5AEA89CFFAEA89CFFAEA89CFFAEA89CFFAEA8 + 9CFFAEA89CFFA8A297F5747068A8252421350000000000000000} + end> + end +end diff --git a/assets/InstructionEditor/Forms/formMain.pas b/assets/InstructionEditor/Forms/formMain.pas new file mode 100644 index 0000000..01bfe89 --- /dev/null +++ b/assets/InstructionEditor/Forms/formMain.pas @@ -0,0 +1,1822 @@ +unit formMain; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.UITypes, + System.ImageList, System.Generics.Collections, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, + Vcl.ImgList, Vcl.StdCtrls, Vcl.ExtCtrls, dxSkinsCore, dxSkinBlue, dxSkinSeven, dxDockPanel, cxOI, + dxSkinsdxBarPainter, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters,cxVGrid, + dxRibbonCustomizationForm, dxRibbonSkins, cxStyles, cxEdit, cxInplaceContainer, dxSkinsForm, + dxStatusBar, dxRibbonStatusBar, cxClasses, dxRibbon, dxBar, dxRibbonForm, cxSplitter, cxPC, + dxBarExtItems, dxSkinsdxDockControlPainter, dxDockControl, dxSkinsdxRibbonPainter, + dxGDIPlusClasses, VirtualTrees, untInstructionEditor; + +// TODO: Add support for multi node selection and allow copy / paste / cut / delete of mutiple +// definitions +// http://www.delphipraxis.net/136601-virtual-treeview-multiselect-onchange-event-problem.html + +// TODO: Update inspector after inspected object changed + +type + TfrmMain = class(TdxRibbonForm) + BarManager: TdxBarManager; + RibbonTab1: TdxRibbonTab; + Ribbon: TdxRibbon; + StatusBar: TdxRibbonStatusBar; + SkinController: TdxSkinController; + barMainManu: TdxBar; + barEditor: TdxBar; + lbLoadDatabase: TdxBarLargeButton; + lbSaveDatabase: TdxBarLargeButton; + imgIcons16: TcxImageList; + imgIcons32: TcxImageList; + lbCreateDefinition: TdxBarLargeButton; + barStatusBarProgress: TdxBar; + piStatusBarProgress: TdxBarProgressItem; + barView: TdxBar; + Splitter: TcxSplitter; + bbDuplicateDefinition: TdxBarButton; + bbDeleteDefinition: TdxBarButton; + barGenerator: TdxBar; + lbGenerate: TdxBarLargeButton; + pnlInspector: TPanel; + DockingManager: TdxDockingManager; + imgMisc: TcxImageList; + DockSite: TdxDockSite; + LayoutDockSite: TdxLayoutDockSite; + pnlPropertyInspector: TdxDockPanel; + Inspector: TcxRTTIInspector; + pnlPropertyInformation: TdxDockPanel; + VertContainerDockSite: TdxVertContainerDockSite; + lblPropertyInfo: TLabel; + popupEditor: TdxRibbonPopupMenu; + dxBarSeparator1: TdxBarSeparator; + dxBarSeparator2: TdxBarSeparator; + dxBarSeparator3: TdxBarSeparator; + bbClipboardCopy: TdxBarButton; + barClipboard: TdxBar; + lbClipboardPaste: TdxBarLargeButton; + bbClipboardCut: TdxBarButton; + lbMnemonicFilter: TdxBarLargeButton; + bbExpandNodes: TdxBarButton; + bbCollapseNodes: TdxBarButton; + barMnemonicFilter: TdxBar; + edtMnemonicFilter: TdxBarEdit; + bbExactMatch: TdxBarButton; + EditorTree: TVirtualStringTree; + imgTreeView: TcxImageList; + dxBarSeparator4: TdxBarSeparator; + bbExpandLeaf: TdxBarButton; + bbCollapseLeaf: TdxBarButton; + Button1: TButton; + Button2: TButton; + procedure FormCreate(Sender: TObject); + procedure FormResize(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure lbLoadDatabaseClick(Sender: TObject); + procedure lbSaveDatabaseClick(Sender: TObject); + procedure EditorTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType; var CellText: string); + procedure EditorTreeChange(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure EditorTreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; + Column: TColumnIndex; var Result: Integer); + procedure EditorTreeCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; + var Allowed: Boolean); + procedure EditorTreePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; + Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); + procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); + procedure lbCreateDefinitionClick(Sender: TObject); + procedure lbGenerateClick(Sender: TObject); + procedure bbDeleteDefinitionClick(Sender: TObject); + procedure InspectorItemChanged(Sender: TObject; AOldRow: TcxCustomRow; AOldCellIndex: Integer); + procedure bbDuplicateDefinitionClick(Sender: TObject); + procedure EditorTreeKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure bbClipboardCopyClick(Sender: TObject); + procedure lbClipboardPasteClick(Sender: TObject); + procedure bbExpandNodesClick(Sender: TObject); + procedure bbCollapseNodesClick(Sender: TObject); + procedure bbClipboardCutClick(Sender: TObject); + procedure lbMnemonicFilterClick(Sender: TObject); + procedure edtMnemonicFilterCurChange(Sender: TObject); + procedure bbExactMatchClick(Sender: TObject); + procedure EditorTreeMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, + Y: Integer); + procedure bbExpandLeafClick(Sender: TObject); + procedure bbCollapseLeafClick(Sender: TObject); + procedure Button1Click(Sender: TObject); + procedure Button2Click(Sender: TObject); + procedure EditorTreeGetImageIndex(Sender: TBaseVirtualTree; + Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; + var Ghosted: Boolean; var ImageIndex: TImageIndex); + strict private + FEditor: TInstructionEditor; + FUpdating: Boolean; + FHasUnsavedChanges: Boolean; + FExpandedFilterProperties: TList; + FExpandedDefinitionProperties: TList; + FInspectorActiveFilterRow: String; + FInspectorActiveDefinitionRow: String; + FEditing: Boolean; + FEditedNode: PVirtualNode; + strict private + procedure EditorWorkStart(Sender: TObject; MinWorkCount, MaxWorkCount: Integer); + procedure EditorWork(Sender: TObject; WorkCount: Integer); + procedure EditorWorkEnd(Sender: TObject); + procedure EditorBeginUpdate(Sender: TObject); + procedure EditorEndUpdate(Sender: TObject); + procedure EditorFilterCreated(Sender: TObject; Filter: TInstructionFilter); + procedure EditorFilterInserted(Sender: TObject; Filter: TInstructionFilter); + procedure EditorFilterChanged(Sender: TObject; Filter: TInstructionFilter); + procedure EditorFilterRemoved(Sender: TObject; Filter: TInstructionFilter); + procedure EditorFilterDestroyed(Sender: TObject; Filter: TInstructionFilter); + procedure EditorDefinitionCreated(Sender: TObject; Definition: TInstructionDefinition); + procedure EditorDefinitionInserted(Sender: TObject; Definition: TInstructionDefinition); + procedure EditorDefinitionChanged(Sender: TObject; Definition: TInstructionDefinition); + procedure EditorDefinitionRemoved(Sender: TObject; Definition: TInstructionDefinition); + procedure EditorDefinitionDestroyed(Sender: TObject; Definition: TInstructionDefinition); + strict private + function GetTreeNode(const Definition: TInstructionDefinition): PVirtualNode; overload; + function GetTreeNode(const Filter: TInstructionFilter): PVirtualNode; overload; + strict private + procedure SetDefaultWindowPosition; inline; + procedure LoadGUIConfiguration; + procedure SaveGUIConfiguration; + procedure UpdateExpandedProperties; + strict private + procedure UpdateControls; + procedure UpdateStatistic; + strict private + procedure ClipboardPaste(Node: PVirtualNode); + procedure ClipboardCopy(Node: PVirtualNode); + procedure ClipboardCut(Node: PVirtualNode); + procedure DefinitionCreate; + procedure DefinitionDuplicate(Node: PVirtualNode); + procedure DefinitionDelete(Node: PVirtualNode); + procedure ExpandAllNodes(Expanded: Boolean); + procedure ExpandLeaf(Node: PVirtualNode; Expanded: Boolean); + procedure SetMnemonicFilter(const Filter: String; ExactMatch: Boolean); + public + { Public-Deklarationen } + end; + +var + frmMain: TfrmMain; + +implementation + +uses + System.IniFiles, Vcl.Clipbrd, SynCrossPlatformJSON, formCreateDefinition, formGenerator, + untHelperClasses, untPropertyHints, + + System.Math; + +{$R *.dfm} + +type + TEditorNodeType = (ntFilterTable, ntInstructionDefinition); + + PEditorNodeData = ^TEditorNodeData; + TEditorNodeData = record + public + NodeType: TEditorNodeType; + case Integer of + 0: (DataClass: TPersistent); + 1: (Filter: TInstructionFilter); + 2: (Definition: TInstructionDefinition); + end; + +{$REGION 'Code: TreeView related methods'} +function TfrmMain.GetTreeNode(const Definition: TInstructionDefinition): PVirtualNode; +begin + // We are using the "data" property to store the corresponding node pointer + Assert(Assigned(Definition.Data)); + Result := Definition.Data; +end; + +function TfrmMain.GetTreeNode(const Filter: TInstructionFilter): PVirtualNode; +begin + // We are using the "data" property to store the corresponding node pointer + Assert(Assigned(Filter.Data)); + Result := Filter.Data; +end; +{$ENDREGION} + +{$REGION 'Code: TreeView related operations'} +procedure TfrmMain.ClipboardCopy(Node: PVirtualNode); + +procedure SaveToJSON(Filter: TInstructionFilter; JSONArray: PJSONVariantData); +var + I: Integer; + JSONObject: TJSONVariantData; +begin + if (Filter.IsDefinitionContainer) then + begin + for I := 0 to (Filter as TDefinitionContainer).DefinitionCount - 1 do + begin + JSONObject.Init; + (Filter as TDefinitionContainer).Definitions[I].SaveToJSON(@JSONObject); + JSONArray^.AddValue(Variant(JSONObject)); + end; + end else + begin + for I := 0 to Filter.Capacity - 1 do + begin + if (Assigned(Filter.Items[I])) then + begin + SaveToJSON(Filter.Items[I], JSONArray); + end; + end; + end; +end; + +var + NodeData: PEditorNodeData; + JSON, + JSONArray, + JSONObject: TJSONVariantData; +begin + NodeData := EditorTree.GetNodeData(Node); + if (Assigned(NodeData)) then + begin + JSONArray.Init; + if (NodeData^.NodeType = ntInstructionDefinition) then + begin + JSONObject.Init; + NodeData^.Definition.SaveToJSON(@JSONObject); + JSONArray.AddValue(Variant(JSONObject)); + end else + begin + if (Application.MessageBox( + 'You are trying to copy multiple definitions to clipboard. Do you want to continue?', + 'Question', MB_ICONQUESTION or MB_YESNO) <> IdYes) then + begin + Exit; + end; + SaveToJSON(NodeData^.Filter, @JSONArray); + end; + JSON.Init; + JSON.AddNameValue('definitions', Variant(JSONArray)); + Clipboard.AsText := TJSONHelper.JSONToString(@JSON); + end; +end; + +procedure TfrmMain.ClipboardCut(Node: PVirtualNode); +begin + ClipboardCopy(Node); + DefinitionDelete(Node); +end; + +procedure TfrmMain.ClipboardPaste(Node: PVirtualNode); +var + JSON: TJSONVariantData; + JSONArray: PJSONVariantData; + I: Integer; + D: TInstructionDefinition; +begin + JSON.Init; + if (JSON.FromJSON(Clipboard.AsText) and (JSON.Kind = jvObject)) then + begin + JSONArray := JSON.Data('definitions'); + if (Assigned(JSONArray) and (JSONArray^.Kind = jvArray)) then + begin + if (JSONArray^.Count > 1) then + begin + if (Application.MessageBox( + 'You are trying to paste multiple definitions from clipboard. Do you want to continue?', + 'Question', MB_ICONQUESTION or MB_YESNO) <> IdYes) then + begin + Exit; + end; + end; + FEditor.BeginUpdate; + try + for I := 0 to JSONArray^.Count - 1 do + begin + D := FEditor.CreateDefinition('unnamed'); + try + D.BeginUpdate; + try + D.Update; + D.LoadFromJSON(JSONVariantDataSafe(JSONArray^.Item[I], jvObject)); + finally + D.EndUpdate; + end; + except + on E: Exception do + begin + D.Free; + Application.MessageBox(PChar(E.Message), 'Error', MB_ICONERROR); + end; + end; + end; + finally + FEditor.EndUpdate; + end; + end; + end; +end; + +procedure TfrmMain.DefinitionCreate; +var + frmCreateDefinition: TfrmCreateDefinition; + D: TInstructionDefinition; +begin + frmCreateDefinition := TfrmCreateDefinition.Create(Application); + try + D := FEditor.CreateDefinition('unnamed'); + D.BeginUpdate; + try + // Force initial position update to cause OnDefinitionInserted for new definitions with + // unchanged (position-relevant) properties. + D.Update; + frmCreateDefinition.Inspector.InspectedObject := D; + frmCreateDefinition.ShowModal; + finally + if (not frmCreateDefinition.Canceled) then D.EndUpdate; + end; + if (frmCreateDefinition.Canceled) then + begin + D.Free; + end; + finally + frmCreateDefinition.Free; + end; +end; + +procedure TfrmMain.DefinitionDelete(Node: PVirtualNode); +var + NextNode: PVirtualNode; + NodeData: PEditorNodeData; +begin + NodeData := EditorTree.GetNodeData(Node); + if Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition) then + begin + NextNode := EditorTree.GetNextSibling(Node); + if (not Assigned(NextNode)) then + begin + NextNode := EditorTree.GetPreviousSibling(Node); + end; + NodeData^.Definition.Free; + if (Assigned(NextNode)) then + begin + EditorTree.FocusedNode := NextNode; + EditorTree.Selected[NextNode] := true; + end; + end; +end; + +procedure TfrmMain.DefinitionDuplicate(Node: PVirtualNode); +var + frmCreateDefinition: TfrmCreateDefinition; + D: TInstructionDefinition; + NodeData: PEditorNodeData; +begin + NodeData := EditorTree.GetNodeData(Node); + if (Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition)) then + begin + frmCreateDefinition := TfrmCreateDefinition.Create(Application); + try + D := FEditor.CreateDefinition('unnamed'); + D.BeginUpdate; + try + // Force initial position update to cause OnDefinitionInserted for new definitions with + // unchanged (position-relevant) properties. + D.Update; + D.Assign(NodeData^.Definition); + frmCreateDefinition.Inspector.InspectedObject := D; + frmCreateDefinition.ShowModal; + finally + if (not frmCreateDefinition.Canceled) then D.EndUpdate; + end; + if (frmCreateDefinition.Canceled) then + begin + D.Free; + end; + finally + frmCreateDefinition.Free; + end; + end; +end; + +procedure TfrmMain.ExpandAllNodes(Expanded: Boolean); +var + Node: PVirtualNode; + NodeData: PEditorNodeData; +begin + EditorTree.BeginUpdate; + try + Node := EditorTree.GetFirst; + while (Assigned(Node)) do + begin + NodeData := EditorTree.GetNodeData(Node); + if (Assigned(NodeData) and (NodeData^.NodeType = ntFilterTable) and + (Assigned(NodeData^.Filter)) and + (not (iffIsRootTable in NodeData^.Filter.FilterFlags))) then + begin + EditorTree.Expanded[Node] := Expanded; + end; + Node := EditorTree.GetNext(Node); + end; + finally + EditorTree.EndUpdate; + end; +end; + +procedure TfrmMain.ExpandLeaf(Node: PVirtualNode; Expanded: Boolean); +begin + // TODO: +end; +{$ENDREGION} + +{$REGION 'Events: Form'} +procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); +var + ID: Integer; +begin + CanClose := true; + if (FHasUnsavedChanges) then + begin + ID := Application.MessageBox('The current database have unsaved changes. Do you' + + ' really want to exit?', 'Question', MB_ICONWARNING or MB_YESNO or MB_DEFBUTTON2); + CanClose := (ID = IdYes); + end; +end; + +procedure TfrmMain.FormCreate(Sender: TObject); +begin + EditorTree.NodeDataSize := SizeOf(TEditorNodeData); + + FExpandedFilterProperties := TList.Create; + FExpandedDefinitionProperties := TList.Create; + + SetDefaultWindowPosition; + LoadGUIConfiguration; + + StatusBar.Panels[1].Visible := false; + + FEditor := TInstructionEditor.Create; + FEditor.OnWorkStart := EditorWorkStart; + FEditor.OnWork := EditorWork; + FEditor.OnWorkEnd := EditorWorkEnd; + FEditor.OnBeginUpdate := EditorBeginUpdate; + FEditor.OnEndUpdate := EditorEndUpdate; + FEditor.OnFilterCreated := EditorFilterCreated; + FEditor.OnFilterInserted := EditorFilterInserted; + FEditor.OnFilterChanged := EditorFilterChanged; + FEditor.OnFilterRemoved := EditorFilterRemoved; + FEditor.OnFilterDestroyed := EditorFilterDestroyed; + FEditor.OnDefinitionCreated := EditorDefinitionCreated; + FEditor.OnDefinitionInserted := EditorDefinitionInserted; + FEditor.OnDefinitionChanged := EditorDefinitionChanged; + FEditor.OnDefinitionRemoved := EditorDefinitionRemoved; + FEditor.OnDefinitionDestroyed := EditorDefinitionDestroyed; + + FEditing := false; + FEditor.Reset; + FEditing := true; + ExpandAllNodes(true); +end; + +procedure TfrmMain.FormDestroy(Sender: TObject); +begin + FEditing := false; + SaveGUIConfiguration; + ExpandAllNodes(false); + if (Assigned(FEditor)) then + begin + FEditor.Free; + end; + if (Assigned(FExpandedFilterProperties)) then + begin + FExpandedFilterProperties.Free; + end; + if (Assigned(FExpandedDefinitionProperties)) then + begin + FExpandedDefinitionProperties.Free; + end; +end; + +procedure TfrmMain.FormResize(Sender: TObject); +begin + piStatusBarProgress.Width := barStatusBarProgress.Control.ClientWidth; +end; +{$ENDREGION} + +{$REGION 'Events: InstructionEditor'} +procedure TfrmMain.EditorBeginUpdate(Sender: TObject); +begin + EditorTree.BeginUpdate; + FUpdating := true; +end; + +procedure TfrmMain.EditorDefinitionChanged(Sender: TObject; Definition: TInstructionDefinition); +begin + EditorTree.RepaintNode(GetTreeNode(Definition)); + UpdateStatistic; + if (FEditing) then + begin + if (not (csDestroying in ComponentState)) and (lbMnemonicFilter.Down) then + begin + SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down); + end; + FHasUnsavedChanges := true; + UpdateControls; + end; +end; + +procedure TfrmMain.EditorDefinitionCreated(Sender: TObject; Definition: TInstructionDefinition); +var + Node: PVirtualNode; + NodeData: PEditorNodeData; +begin + Node := EditorTree.AddChild(nil); + Definition.Data := Node; + EditorTree.IsVisible[Node] := false; + NodeData := EditorTree.GetNodeData(Node); + NodeData^.NodeType := ntInstructionDefinition; + NodeData^.Definition := Definition; + UpdateStatistic; +end; + +procedure TfrmMain.EditorDefinitionDestroyed(Sender: TObject; Definition: TInstructionDefinition); +begin + EditorTree.DeleteNode(GetTreeNode(Definition)); + if (Inspector.InspectedObject = Definition) then + begin + Inspector.InspectedObject := nil; + end; + UpdateStatistic; +end; + +procedure TfrmMain.EditorDefinitionInserted(Sender: TObject; Definition: TInstructionDefinition); +var + Node: PVirtualNode; +begin + Assert(Assigned(Definition.Parent)); + Node := GetTreeNode(Definition); + EditorTree.IsVisible[Node] := true; + EditorTree.MoveTo(Node, GetTreeNode(Definition.Parent), amAddChildLast, false); + if (FEditing) then + begin + FEditedNode := Node; + FHasUnsavedChanges := true; + UpdateControls; + end; +end; + +procedure TfrmMain.EditorDefinitionRemoved(Sender: TObject; Definition: TInstructionDefinition); +var + Node: PVirtualNode; +begin + Node := GetTreeNode(Definition); + EditorTree.IsVisible[Node] := false; + EditorTree.MoveTo(Node, nil, amInsertAfter, false); + if (FEditing) then + begin + if (EditorTree.FocusedNode = Node) then + begin + EditorTree.FocusedNode := nil; + EditorTree.Selected[Node] := false; + end; + FHasUnsavedChanges := true; + UpdateControls; + end; +end; + +procedure TfrmMain.EditorEndUpdate(Sender: TObject); +begin + EditorTree.EndUpdate; + FUpdating := false; + if (FEditing) and Assigned(FEditedNode) then + begin + EditorTree.FocusedNode := FEditedNode; + EditorTree.Selected[FEditedNode] := true; + EditorTree.ScrollIntoView(FEditedNode, true); + FEditedNode := nil; + end; + UpdateStatistic; +end; + +procedure TfrmMain.EditorFilterChanged(Sender: TObject; Filter: TInstructionFilter); +begin + EditorTree.RepaintNode(GetTreeNode(Filter)); +end; + +procedure TfrmMain.EditorFilterCreated(Sender: TObject; Filter: TInstructionFilter); +var + Node: PVirtualNode; + NodeData: PEditorNodeData; +begin + Node := EditorTree.AddChild(nil); + Filter.Data := Node; + if (not (iffIsRootTable in Filter.FilterFlags)) then + begin + EditorTree.IsVisible[Node] := false; + end; + NodeData := EditorTree.GetNodeData(Node); + NodeData^.NodeType := ntFilterTable; + NodeData^.Filter := Filter; + UpdateStatistic; +end; + +procedure TfrmMain.EditorFilterDestroyed(Sender: TObject; Filter: TInstructionFilter); +begin + EditorTree.DeleteNode(GetTreeNode(Filter)); + if (Inspector.InspectedObject = Filter) then + begin + Inspector.InspectedObject := nil; + end; + UpdateStatistic; +end; + +procedure TfrmMain.EditorFilterInserted(Sender: TObject; Filter: TInstructionFilter); +var + Node, ParentNode: PVirtualNode; +begin + Assert(Assigned(Filter.Parent)); + Node := GetTreeNode(Filter); + ParentNode := GetTreeNode(Filter.Parent); + EditorTree.MoveTo(Node, ParentNode, amAddChildLast, false); + EditorTree.IsVisible[Node] := true; + // Expand root table after first filter insertion + if (iffIsRootTable in Filter.Parent.FilterFlags) and (Filter.Parent.ItemCount = 1) then + begin + EditorTree.Expanded[ParentNode] := true; + end; +end; + +procedure TfrmMain.EditorFilterRemoved(Sender: TObject; Filter: TInstructionFilter); +var + Node: PVirtualNode; +begin + Node := GetTreeNode(Filter); + EditorTree.IsVisible[Node] := false; + EditorTree.MoveTo(Node, nil, amInsertAfter, false); + if (FEditing) then + begin + if (EditorTree.FocusedNode = Node) then + begin + EditorTree.FocusedNode := nil; + EditorTree.Selected[Node] := false; + end; + UpdateControls; + end; +end; + +procedure TfrmMain.EditorWork(Sender: TObject; WorkCount: Integer); +begin + piStatusBarProgress.Position := WorkCount; + if ((WorkCount mod piStatusBarProgress.Tag) = 0) then + begin + Application.ProcessMessages; + end; +end; + +procedure TfrmMain.EditorWorkEnd(Sender: TObject); +begin + piStatusBarProgress.Visible := ivNever; +end; + +procedure TfrmMain.EditorWorkStart(Sender: TObject; MinWorkCount, MaxWorkCount: Integer); +begin + piStatusBarProgress.Min := MinWorkCount; + piStatusBarProgress.Max := MaxWorkCount; + piStatusBarProgress.Tag := Round((MaxWorkCount - MinWorkCount) / 100) + 1; + piStatusBarProgress.Position := 0; + piStatusBarProgress.Visible := ivAlways; +end; +{$ENDREGION} + +{$REGION 'Events: TreeView'} +procedure TfrmMain.EditorTreeChange(Sender: TBaseVirtualTree; Node: PVirtualNode); +var + NodeData: PEditorNodeData; + I: Integer; +begin + UpdateExpandedProperties; + Inspector.BeginUpdate; + try + if (Assigned(Inspector.FocusedRow)) then + begin + if (Inspector.InspectedObject is TInstructionFilter) then + begin + FInspectorActiveFilterRow := + (Inspector.FocusedRow as TcxPropertyRow).PropertyEditor.GetName; + end else if (Inspector.InspectedObject is TInstructionDefinition) then + begin + FInspectorActiveDefinitionRow := + (Inspector.FocusedRow as TcxPropertyRow).PropertyEditor.GetName; + end; + end; + Inspector.InspectedObject := nil; + NodeData := Sender.GetNodeData(Node); + if Assigned(NodeData) then + begin + Inspector.InspectedObject := NodeData^.DataClass; + for I := 0 to Inspector.Rows.Count - 1 do + begin + if ((NodeData^.NodeType = ntFilterTable) and FExpandedFilterProperties.Contains( + (Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) or + ((NodeData^.NodeType = ntInstructionDefinition) and + FExpandedDefinitionProperties.Contains( + (Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) then + begin + Inspector.Rows[I].Expanded := true; + end; + if ((NodeData^.NodeType = ntFilterTable) and (FInspectorActiveFilterRow = + (Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) or + ((NodeData^.NodeType = ntInstructionDefinition) and + (FInspectorActiveDefinitionRow = + (Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) then + begin + Inspector.FocusedRow := Inspector.Rows[I]; + end; + end; + end; + finally + Inspector.EndUpdate; + end; + UpdateControls; +end; + +procedure TfrmMain.EditorTreeCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode; + var Allowed: Boolean); +var + NodeData: PEditorNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData) and (NodeData^.NodeType = ntFilterTable) and + Assigned(NodeData^.Filter) and (iffIsRootTable in NodeData^.Filter.FilterFlags)) then + begin + Allowed := false; + end; +end; + +procedure TfrmMain.EditorTreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; + Column: TColumnIndex; var Result: Integer); +var + NodeDataA, + NodeDataB: PEditorNodeData; +begin + NodeDataA := Sender.GetNodeData(Node1); + NodeDataB := Sender.GetNodeData(Node2); + if (NodeDataA^.NodeType <> NodeDataB^.NodeType) then Exit; + if (Assigned(NodeDataA) and Assigned(NodeDataB) and + Assigned(NodeDataA^.DataClass) and Assigned(NodeDataB^.DataClass)) then + begin + case NodeDataA^.NodeType of + ntFilterTable: + begin + Assert(NodeDataB^.NodeType = ntFilterTable); + if (Assigned(NodeDataA^.Filter.Parent)) then + begin + Assert(Assigned(NodeDataB^.Filter.Parent)); + Assert(NodeDataA^.Filter.Parent = NodeDataB^.Filter.Parent); + Result := NodeDataA^.Filter.Parent.IndexOf(NodeDataA^.Filter) - + NodeDataB^.Filter.Parent.IndexOf(NodeDataB^.Filter); + end; + end; + ntInstructionDefinition: + begin + Assert(NodeDataB^.NodeType = ntInstructionDefinition); + Result := CompareStr(NodeDataA^.Definition.Mnemonic, NodeDataB^.Definition.Mnemonic); + end; + end; + end; +end; + +procedure TfrmMain.EditorTreeGetImageIndex(Sender: TBaseVirtualTree; + Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; + var Ghosted: Boolean; var ImageIndex: TImageIndex); +var + NodeData: PEditorNodeData; +begin + if (Column <> 0) or (Kind = ikOverlay) then + begin + Exit; + end; + NodeData := Sender.GetNodeData(Node); + if Assigned(NodeData) then + begin + case NodeData^.NodeType of + ntFilterTable: + begin + ImageIndex := 0; + if (NodeData^.Filter is TDefinitionContainer) then + begin + ImageIndex := 1; + end; + end; + ntInstructionDefinition: + begin + ImageIndex := 2; + end; + end; + end; +end; + +procedure TfrmMain.EditorTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); +var + NodeData: PEditorNodeData; + S: String; +begin + CellText := ''; + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData) and Assigned(NodeData^.DataClass)) then + begin + case (NodeData^.NodeType) of + ntFilterTable: + begin + if (TextType <> ttNormal) and (not (Column in [0])) then Exit; + case Column of + 0: + begin + case TextType of + ttNormal: + begin + if (not Assigned(NodeData^.Filter.Parent)) then + begin + CellText := 'Root'; + end else + begin + CellText := IntToHex(NodeData^.Filter.Parent.IndexOf(NodeData^.Filter), 2); + end; + end; + ttStatic: + begin + if (Assigned(NodeData^.Filter.Parent)) then + begin + S := NodeData^.Filter.Parent.GetItemDescription( + NodeData^.Filter.Parent.IndexOf(NodeData^.Filter)); + if (S <> '') then + begin + CellText := '(' + S + ')'; + end; + end; + end; + end; + end; + end; + end; + ntInstructionDefinition: + begin + if (TextType <> ttNormal) and (not (Column in [0, 1])) then Exit; + case Column of + 0: + begin + case TextType of + ttNormal: CellText := IntToHex(Node.Index, 2); + ttStatic: CellText := 'Definition'; + end; + end; + 1: + begin + case TextType of + ttNormal: + begin + CellText := IntToHex(NodeData^.Definition.Opcode, 2); + end; + ttStatic: + begin + CellText := ''; // TODO: + end + end; + end; + 2: CellText := NodeData^.Definition.Mnemonic; + 3: CellText := NodeData^.Definition.Operands.OperandA.GetDescription(true); + 4: CellText := NodeData^.Definition.Operands.OperandB.GetDescription(true); + 5: CellText := NodeData^.Definition.Operands.OperandC.GetDescription(true); + 6: CellText := NodeData^.Definition.Operands.OperandD.GetDescription(true); + 7: CellText := NodeData^.Definition.Comment; + end; + end; + end; + end; +end; + +procedure TfrmMain.EditorTreeKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + +procedure CopyOperands; +var + NodeData: PEditorNodeData; + I: Integer; + S: String; + O: TInstructionOperand; +begin + NodeData := EditorTree.GetNodeData(EditorTree.FocusedNode); + if (Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition)) then + begin + S := ''; + for I := 0 to 3 do + begin + O := nil; + case I of + 0: O := NodeData^.Definition.Operands.OperandA; + 1: O := NodeData^.Definition.Operands.OperandB; + 2: O := NodeData^.Definition.Operands.OperandC; + 3: O := NodeData^.Definition.Operands.OperandD; + end; + S := S + IntToStr(Integer(O.OperandType)) + ',' + IntToStr(Integer(O.Encoding)) + ',' + + IntToStr(Integer(O.AccessMode)) + ','; + end; + Clipboard.AsText := S; + end; +end; + +procedure PasteOperands; +var + NodeData: PEditorNodeData; + A: TArray; + I, J: Integer; + O: TInstructionOperand; +begin + NodeData := EditorTree.GetNodeData(EditorTree.FocusedNode); + if (Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition)) then + begin + A := Clipboard.AsText.Split([',']); + if (Length(A) >= 12) then + begin + I := 0; + J := 0; + while (J < 4) do + begin + O := nil; + case J of + 0: O := NodeData^.Definition.Operands.OperandA; + 1: O := NodeData^.Definition.Operands.OperandB; + 2: O := NodeData^.Definition.Operands.OperandC; + 3: O := NodeData^.Definition.Operands.OperandD; + end; + O.OperandType := TOperandType(StrToInt(A[I])); + O.Encoding := TOperandEncoding(StrToInt(A[I + 1])); + O.AccessMode := TOperandAccessMode(StrToInt(A[I + 2])); + Inc(I, 3); + Inc(J); + end; + end; + end; +end; + +begin + if (ssCtrl in Shift) then + begin + case Key of + Ord('V'): + lbClipboardPaste.Click; + Ord('C'): + bbClipboardCopy.Click; + Ord('X'): + bbClipboardCut.Click; + Ord('F'): + lbMnemonicFilter.Click; + Ord('E'): + CopyOperands; + Ord('R'): + PasteOperands; + end; + end else if (Shift = []) then + begin + case Key of + VK_DELETE: + bbDeleteDefinition.Click; + end; + end; +end; + +procedure TfrmMain.EditorTreeMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, + Y: Integer); +begin + if (Button = mbRight) then + begin + popupEditor.PopupFromCursorPos; + end; +end; + +procedure TfrmMain.EditorTreePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; + Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); +var + NodeData: PEditorNodeData; +begin + NodeData := Sender.GetNodeData(Node); + if (Assigned(NodeData) and Assigned(NodeData^.DataClass)) then + begin + case NodeData^.NodeType of + ntFilterTable: + begin + if (NodeData^.Filter.HasConflicts) then + begin + TargetCanvas.Font.Color := clRed; + Exit; + end; + end; + ntInstructionDefinition: + begin + if (NodeData^.Definition.HasConflicts) then + begin + TargetCanvas.Font.Color := clRed; + Exit; + end; + end; + end; + end; + case Column of + 0: + begin + case TextType of + ttNormal: ; + ttStatic: TargetCanvas.Font.Color := clGray; + end; + end; + 1: + begin + case TextType of + ttNormal: ; + ttStatic: TargetCanvas.Font.Color := clGray; + end; + end; + end; +end; +{$ENDREGION} + +procedure TfrmMain.bbClipboardCopyClick(Sender: TObject); +begin + ClipboardCopy(EditorTree.FocusedNode); +end; + +procedure TfrmMain.bbClipboardCutClick(Sender: TObject); +begin + ClipboardCut(EditorTree.FocusedNode); +end; + +procedure TfrmMain.bbDuplicateDefinitionClick(Sender: TObject); +begin + DefinitionDuplicate(EditorTree.FocusedNode); +end; + +procedure TfrmMain.bbExactMatchClick(Sender: TObject); +begin + SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down); +end; + +procedure TfrmMain.bbExpandLeafClick(Sender: TObject); +begin + ExpandLeaf(EditorTree.FocusedNode, true); +end; + +procedure TfrmMain.bbExpandNodesClick(Sender: TObject); +begin + ExpandAllNodes(true); +end; + +procedure TfrmMain.Button1Click(Sender: TObject); + +procedure DeleteDuplicates(T: TInstructionFilter); +var + L: TList; + D: TInstructionDefinition; + I, J: Integer; + B: Boolean; +begin + if (T is TDefinitionContainer) then + begin + L := TList.Create; + try + for I := (T as TDefinitionContainer).DefinitionCount - 1 downto 0 do + begin + D := (T as TDefinitionContainer).Definitions[I]; + B := true; + for J := 0 to L.Count - 1 do + begin + if ((D.Mnemonic = L[J].Mnemonic) and D.Operands.Equals(L[J].Operands) and + D.CPUID.Equals(L[J].CPUID) and (D.EVEXCD8Scale = L[J].EVEXCD8Scale)) then + begin + L[J].Flags := L[J].Flags + D.Flags; + D.Free; + B := false; + Break; + end; + end; + if (B) then L.Add(D); + end; + finally + L.Free; + end; + end else + begin + for I := 0 to T.Capacity - 1 do + begin + if Assigned(T.Items[I]) then + begin + DeleteDuplicates(T.Items[I]); + end; + end; + end; +end; + +var + I, J, K, RegCount, MemIndex: Integer; + S: String; + A: TArray; + D: TInstructionDefinition; + O: TInstructionOperand; +begin + FEditor.BeginUpdate; + + for I := 0 to FEditor.DefinitionCount - 1 do + begin + D := FEditor.Definitions[I]; + if (D.Encoding <> ieEVEX) then Continue; + S := D.Comment; + J := 1; + while (J < Length(S)) and (S[J] <> ' ') do Inc(J); + Delete(S, 1, J); + A := S.Split([',']); + for J := Low(A) to High(A) do + begin + A[J] := Trim(A[J]); + end; + S := A[High(A)]; + J := 1; + while (J < Length(S)) and (S[J] <> ' ') do Inc(J); + Delete(S, J, Length(S)); + A[High(A)] := S; + if (A[High(A)][1] = '(') then SetLength(A, Length(A) - 1); + + RegCount := 0; + MemIndex := -1; + D.BeginUpdate; + for J := Low(A) to High(A) do + begin + O := nil; + case J of + 0: O := D.Operands.OperandA; + 1: O := D.Operands.OperandB; + 2: O := D.Operands.OperandC; + 3: O := D.Operands.OperandD; + end; + + if (Pos('{1to', A[J]) > 0) then D.Flags := D.Flags + [ifHasEVEXBC]; + if (Pos('{sae}', A[J]) > 0) then D.Flags := D.Flags + [ifHasEVEXSAE]; + + if (Pos('VK1', A[J]) > 0) then O.OperandType := optMSKR; + if (Pos('VK2', A[J]) > 0) then O.OperandType := optMSKR; + if (Pos('VK4', A[J]) > 0) then O.OperandType := optMSKR; + if (Pos('VK8', A[J]) > 0) then O.OperandType := optMSKR; + if (Pos('VK16', A[J]) > 0) then O.OperandType := optMSKR; + if (Pos('VK32', A[J]) > 0) then O.OperandType := optMSKR; + if (Pos('VK64', A[J]) > 0) then O.OperandType := optMSKR; + + if (Pos('GR8', A[J]) > 0) then O.OperandType := optGPR8; + if (Pos('GR16', A[J]) > 0) then O.OperandType := optGPR16; + if (Pos('GR32', A[J]) > 0) then O.OperandType := optGPR32; + if (Pos('GR64', A[J]) > 0) then O.OperandType := optGPR64; + if (Pos('8mem', A[J]) > 0) then O.OperandType := optMem8; + if (Pos('16mem', A[J]) > 0) then O.OperandType := optMem16; + if (Pos('32mem', A[J]) > 0) then + begin + if (Pos('{1to2}', A[J]) > 0) then O.OperandType := optMem32Bcst2 else + if (Pos('{1to4}', A[J]) > 0) then O.OperandType := optMem32Bcst4 else + if (Pos('{1to8}', A[J]) > 0) then O.OperandType := optMem32Bcst8 else + if (Pos('{1to16}', A[J]) > 0) then O.OperandType := optMem32Bcst16 else + O.OperandType := optMem32; + end; + if (Pos('64mem', A[J]) > 0) then + begin + if (Pos('{1to2}', A[J]) > 0) then O.OperandType := optMem64Bcst2 else + if (Pos('{1to4}', A[J]) > 0) then O.OperandType := optMem64Bcst4 else + if (Pos('{1to8}', A[J]) > 0) then O.OperandType := optMem64Bcst8 else + if (Pos('{1to16}', A[J]) > 0) then O.OperandType := optMem64Bcst16 else + O.OperandType := optMem64; + end; + + if (Pos('VR128', A[J]) > 0) then O.OperandType := optVR128; + if (Pos('VR256', A[J]) > 0) then O.OperandType := optVR256; + if (Pos('VR512', A[J]) > 0) then O.OperandType := optVR512; + if (Pos('128mem', A[J]) > 0) then O.OperandType := optMem128; + if (Pos('256mem', A[J]) > 0) then O.OperandType := optMem256; + if (Pos('512mem', A[J]) > 0) then O.OperandType := optMem512; + + if (Pos('vx32', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vx64', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vx128', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vx256', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vx512', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vy32', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vy64', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vy128', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vy256', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vy512', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vz32', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vz64', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vz128', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vz256', A[J]) > 0) then O.Encoding := opeNone; + if (Pos('vz512', A[J]) > 0) then O.Encoding := opeNone; + + if (Pos('imm8', A[J]) > 0) then O.OperandType := optImm8; + if (Pos('imm8u', A[J]) > 0) then O.OperandType := optImm8U; + if (Pos('u8imm', A[J]) > 0) then O.OperandType := optImm8U; + + if (O.Encoding = opeModrmRm) then + begin + MemIndex := J; + if (D.EVEXCD8Scale <> 0) then + begin + case D.EvexCD8Scale of + 1: O.Encoding := opeModrmRmCD1; + 2: O.Encoding := opeModrmRmCD2; + 4: O.Encoding := opeModrmRmCD4; + 8: O.Encoding := opeModrmRmCD8; + 16: O.Encoding := opeModrmRmCD16; + 32: O.Encoding := opeModrmRmCD32; + 64: O.Encoding := opeModrmRmCD64; + end; + end; + end; + + if (O.Encoding = opeModrmReg) then Inc(RegCount); + end; + D.Operands.OperandA.AccessMode := opaWrite; + if (RegCount = 3) or ((RegCount = 2) and (MemIndex > -1) and (MemIndex <> 1)) then + begin + D.Operands.OperandB.Encoding := opeVexVVVV; + end; + D.EndUpdate; + + end; + DeleteDuplicates(FEditor.RootTable.Items[$62]); + FEditor.EndUpdate; +end; + +procedure TfrmMain.Button2Click(Sender: TObject); + +function BitsNeeded(N: Integer): Integer; +begin + Result := Floor(log2(n) + 1); +end; + +var + Mnemonics: TDictionary; + Node: PVirtualNode; + NodeData: PEditorNodeData; + LOPS: TList>; + LCPUID: TList>; + LEFLAGS: TList>; + LREGS: TList>; + I, J, Bits: Integer; + B: Boolean; + POPS: TPair; + PCPUID: TPair; + PEFLAGS: TPair; + PREGS: TPair; +begin + Bits := 4; // EVEX Info + + Mnemonics := TDictionary.Create; + try + Node := EditorTree.GetFirst; + while Assigned(Node) do + begin + NodeData := EditorTree.GetNodeData(Node); + if (NodeData^.NodeType = ntInstructionDefinition) then + begin + if (not Mnemonics.ContainsKey(NodeData^.Definition.Mnemonic)) then + begin + Mnemonics.Add(NodeData^.Definition.Mnemonic, true); + end; + end; + Node := EditorTree.GetNext(Node); + end; + ShowMessage('Mnemonics: ' + IntToStr(Mnemonics.Count) + ' (' + IntToStr(BitsNeeded(Mnemonics.Count)) + ' bit)'); + Inc(Bits, BitsNeeded(Mnemonics.Count)); + finally + Mnemonics.Free; + end; + + LOPS := TList>.Create; + for I := 0 to FEditor.DefinitionCount - 1 do + begin + B := false; + for J := 0 to LOPS.Count - 1 do + begin + if (LOPS[J].Key.Equals(FEditor.Definitions[I].Operands)) then + begin + POPS := LOPS[J]; + Inc(POPS.Value); + LOPS[J] := POPS; + B := true; + Break; + end; + end; + if (not B) then + begin + POPS.Key := FEditor.Definitions[I].Operands; + POPS.Value := 1; + LOPS.Add(POPS); + end; + end; + ShowMessage('OPS: ' + IntToStr(LOPS.Count) + ' (' + IntToStr(BitsNeeded(LOPS.Count)) + ' bit)'); + Inc(Bits, BitsNeeded(LOPS.Count)); + LOPS.Free; + + LCPUID := TList>.Create; + for I := 0 to FEditor.DefinitionCount - 1 do + begin + B := false; + for J := 0 to LOPS.Count - 1 do + begin + if (LCPUID[J].Key = FEditor.Definitions[I].CPUID.FeatureFlags) then + begin + PCPUID := LCPUID[J]; + Inc(PCPUID.Value); + LCPUID[J] := PCPUID; + B := true; + Break; + end; + end; + if (not B) then + begin + PCPUID.Key := FEditor.Definitions[I].CPUID.FeatureFlags; + PCPUID.Value := 1; + LCPUID.Add(PCPUID); + end; + end; + ShowMessage('CPUID: ' + IntToStr(LCPUID.Count) + ' (' + IntToStr(BitsNeeded(LCPUID.Count)) + ' bit)'); + Inc(Bits, BitsNeeded(LCPUID.Count)); + LCPUID.Free; + + LEFLAGS := TList>.Create; + for I := 0 to FEditor.DefinitionCount - 1 do + begin + B := false; + for J := 0 to LOPS.Count - 1 do + begin + if (LEFLAGS[J].Key.Equals(FEditor.Definitions[I].X86Flags)) then + begin + PEFLAGS := LEFLAGS[J]; + Inc(PEFLAGS.Value); + LEFLAGS[J] := PEFLAGS; + B := true; + Break; + end; + end; + if (not B) then + begin + PEFLAGS.Key := FEditor.Definitions[I].X86Flags; + PEFLAGS.Value := 1; + LEFLAGS.Add(PEFLAGS); + end; + end; + ShowMessage('EFLAGS: ' + IntToStr(LEFLAGS.Count) + ' (' + IntToStr(BitsNeeded(LEFLAGS.Count)) + ' bit)'); + Inc(Bits, BitsNeeded(LEFLAGS.Count)); + LEFLAGS.Free; + + LREGS := TList>.Create; + for I := 0 to FEditor.DefinitionCount - 1 do + begin + B := false; + for J := 0 to LOPS.Count - 1 do + begin + if (LREGS[J].Key = FEditor.Definitions[I].ImplicitRead.Registers) then + begin + PREGS := LREGS[J]; + Inc(PREGS.Value); + LREGS[J] := PREGS; + B := true; + Break; + end; + end; + if (not B) then + begin + PREGS.Key := FEditor.Definitions[I].ImplicitRead.Registers; + PREGS.Value := 1; + LREGS.Add(PREGS); + end; + B := false; + for J := 0 to LOPS.Count - 1 do + begin + if (LREGS[J].Key = FEditor.Definitions[I].ImplicitWrite.Registers) then + begin + PREGS := LREGS[J]; + Inc(PREGS.Value); + LREGS[J] := PREGS; + B := true; + Break; + end; + end; + if (not B) then + begin + PREGS.Key := FEditor.Definitions[I].ImplicitWrite.Registers; + PREGS.Value := 1; + LREGS.Add(PREGS); + end; + end; + ShowMessage('REGS: ' + IntToStr(LREGS.Count) + ' (' + IntToStr(BitsNeeded(LREGS.Count)) + ' bit)'); + Inc(Bits, BitsNeeded(LREGS.Count)); + LEFLAGS.Free; + + ShowMessage('BytesNeeded: ' + IntToStr(Ceil(Bits / 8)) + ' (' + IntToStr(Bits) + ' bits)'); +end; + +procedure TfrmMain.bbCollapseLeafClick(Sender: TObject); +begin + ExpandLeaf(EditorTree.FocusedNode, false); +end; + +procedure TfrmMain.bbCollapseNodesClick(Sender: TObject); +begin + ExpandAllNodes(false); +end; + +procedure TfrmMain.bbDeleteDefinitionClick(Sender: TObject); +begin + DefinitionDelete(EditorTree.FocusedNode); +end; + +procedure TfrmMain.edtMnemonicFilterCurChange(Sender: TObject); +begin + // TODO: Filter is offsync, if the user leaves the edit by pressing ESC or focusing an other + // control + SetMnemonicFilter(edtMnemonicFilter.CurText, bbExactMatch.Down); +end; + +procedure TfrmMain.lbClipboardPasteClick(Sender: TObject); +begin + ClipboardPaste(EditorTree.FocusedNode); +end; + +procedure TfrmMain.lbCreateDefinitionClick(Sender: TObject); +begin + DefinitionCreate; +end; + +procedure TfrmMain.lbGenerateClick(Sender: TObject); + +procedure DeleteDuplicates(T: TInstructionFilter); +var + L: TList; + D: TInstructionDefinition; + I, J: Integer; + B: Boolean; +begin + if (T is TDefinitionContainer) then + begin + L := TList.Create; + try + for I := (T as TDefinitionContainer).DefinitionCount - 1 downto 0 do + begin + D := (T as TDefinitionContainer).Definitions[I]; + B := true; + for J := 0 to L.Count - 1 do + begin + if (D.Equals(L[J])) then + begin + D.Free; + B := false; + Break; + end; + end; + if (B) then L.Add(D); + end; + finally + L.Free; + end; + end else + begin + for I := 0 to T.Capacity - 1 do + begin + if Assigned(T.Items[I]) then + begin + DeleteDuplicates(T.Items[I]); + end; + end; + end; +end; + +var + frmGenerator: TfrmGenerator; +begin + frmGenerator := TfrmGenerator.Create(Application); + try + frmGenerator.Editor := FEditor; + frmGenerator.ShowModal; + finally + frmGenerator.Free; + end; + + Exit; + FEditor.BeginUpdate; + DeleteDuplicates(FEditor.RootTable); + FEditor.EndUpdate; +end; + +procedure TfrmMain.lbLoadDatabaseClick(Sender: TObject); +var + ID: Integer; +begin + if (FHasUnsavedChanges) then + begin + ID := Application.MessageBox('Reloading the database will revert all unsaved changes. Do you' + + ' really want to continue?', 'Question', MB_ICONWARNING or MB_YESNO or MB_DEFBUTTON2); + if (ID <> IdYes) then + begin + Exit; + end; + end; + FEditing := false; + try + ExpandAllNodes(false); + FEditor.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'instructions.json'); + if (lbMnemonicFilter.Down) then + begin + SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down); + end; + except + on E: Exception do + begin + Application.MessageBox(PChar(E.Message), 'Error', MB_ICONERROR); + end; + end; + FEditing := true; + FHasUnsavedChanges := false; + UpdateControls; +end; + +procedure TfrmMain.lbMnemonicFilterClick(Sender: TObject); +begin + StatusBar.Panels[1].Visible := lbMnemonicFilter.Down; + piStatusBarProgress.Width := barStatusBarProgress.Control.ClientWidth; + if (lbMnemonicFilter.Down) then + begin + SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down); + edtMnemonicFilter.SetFocus; + end else + begin + SetMnemonicFilter('', false); + end; +end; + +procedure TfrmMain.lbSaveDatabaseClick(Sender: TObject); +begin + FEditor.SaveToFile(ExtractFilePath(ParamStr(0)) + 'instructions.json'); + FHasUnsavedChanges := false; + UpdateControls; +end; + +procedure TfrmMain.LoadGUIConfiguration; +var + Ini: TIniFile; + I: Integer; + A: TArray; +begin + DockingManager.LoadLayoutFromIniFile(ChangeFileExt(ParamStr(0), 'Layout.ini')); + Ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini')); + try + for I := 0 to EditorTree.Header.Columns.Count - 1 do + begin + EditorTree.Header.Columns[I].Width := Ini.ReadInteger('Editor', + Format('Col_%.2d_Width', [I]), EditorTree.Header.Columns[I].Width); + end; + A := Ini.ReadString('Inspector', 'ExpandedFilterProperties', '').Split([',']); + for I := Low(A) to High(A) do + begin + FExpandedFilterProperties.Add(A[I]); + end; + A := Ini.ReadString('Inspector', 'ExpandedDefinitionProperties', '').Split([',']); + for I := Low(A) to High(A) do + begin + FExpandedDefinitionProperties.Add(A[I]); + end; + pnlInspector.Width := Ini.ReadInteger('Inspector', 'Width', 364); + Inspector.OptionsView.RowHeaderWidth := Ini.ReadInteger('Inspector', 'RowHeaderWidth', 170); + finally + Ini.Free; + end; +end; + +procedure TfrmMain.SaveGUIConfiguration; +var + Ini: TIniFile; + I: Integer; + S: String; +begin + DockingManager.SaveLayoutToIniFile(ChangeFileExt(ParamStr(0), 'Layout.ini')); + Ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini')); + try + for I := 0 to EditorTree.Header.Columns.Count - 1 do + begin + Ini.WriteInteger('Editor', Format('Col_%.2d_Width', [I]), EditorTree.Header.Columns[I].Width); + end; + UpdateExpandedProperties; + S := ''; + for I := 0 to FExpandedFilterProperties.Count - 1 do + begin + S := S + FExpandedFilterProperties[I]; + if (I < FExpandedFilterProperties.Count - 1) then + begin + S := S + ','; + end; + end; + Ini.WriteString('Inspector', 'ExpandedFilterProperties', S); + S := ''; + for I := 0 to FExpandedDefinitionProperties.Count - 1 do + begin + S := S + FExpandedDefinitionProperties[I]; + if (I < FExpandedDefinitionProperties.Count - 1) then + begin + S := S + ','; + end; + end; + Ini.WriteString('Inspector', 'ExpandedDefinitionProperties', S); + Ini.WriteInteger('Inspector', 'Width', pnlInspector.Width); + Ini.WriteInteger('Inspector', 'RowHeaderWidth', Inspector.OptionsView.RowHeaderWidth); + finally + Ini.Free; + end; +end; + +procedure TfrmMain.SetDefaultWindowPosition; +var + R: TRect; +begin + R := Screen.MonitorFromPoint(Mouse.CursorPos).WorkareaRect; + SetBounds(R.Left + 50, R.Top + 50, R.Width - 100, R.Height - 100); +end; + +procedure TfrmMain.SetMnemonicFilter(const Filter: String; ExactMatch: Boolean); + +procedure ApplyMnemonicFilter(Filter: TInstructionFilter; out IsVisible: Boolean; + const FilterText: String; FilterLength: Integer); +var + D: TInstructionDefinition; + C: TDefinitionContainer; + I: Integer; + B: Boolean; +begin + IsVisible := (FilterLength = 0); + if (iffIsDefinitionContainer in Filter.FilterFlags) then + begin + C := (Filter as TDefinitionContainer); + for I := 0 to C.DefinitionCount - 1 do + begin + B := IsVisible; + D := C.Definitions[I]; + if (not IsVisible) then + begin + if (Length(D.Mnemonic) >= FilterLength) then + begin + if (ExactMatch) then + begin + B := (CompareStr(FilterText, LowerCase(D.Mnemonic)) = 0); + end else + begin + B := (CompareStr(FilterText, AnsiLowerCase(Copy(D.Mnemonic, 1, FilterLength))) = 0); + end; + end; + end; + EditorTree.IsVisible[GetTreeNode(D)] := B; + IsVisible := IsVisible or B; + end; + end else + begin + for I := 0 to Filter.Capacity - 1 do + begin + if (not Assigned(Filter.Items[I])) then Continue; + ApplyMnemonicFilter(Filter.Items[I], B, FilterText, FilterLength); + EditorTree.IsVisible[GetTreeNode(Filter.Items[I])] := B; + IsVisible := IsVisible or B; + end; + EditorTree.IsVisible[GetTreeNode(Filter)] := IsVisible; + end; +end; + +var + FilterText: String; + FilterLength: Integer; + IsVisible: Boolean; +begin + EditorTree.BeginUpdate; + try + FilterText := AnsiLowerCase(Filter); + FilterLength := Length(Filter); + ApplyMnemonicFilter(FEditor.RootTable, IsVisible, FilterText, FilterLength); + finally + EditorTree.EndUpdate; + end; +end; + +procedure TfrmMain.UpdateControls; +var + NodeData: PEditorNodeData; +begin + lbSaveDatabase.Enabled := FHasUnsavedChanges; + + NodeData := EditorTree.GetNodeData(EditorTree.FocusedNode); + bbDuplicateDefinition.Enabled := + Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition); + bbDeleteDefinition.Enabled := + Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition); + bbClipboardCopy.Enabled := + Assigned(NodeData) {and (NodeData^.NodeType = ntInstructionDefinition)}; + bbClipboardCut.Enabled := + Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition); + bbExpandLeaf.Enabled := + Assigned(NodeData) and (NodeData^.NodeType <> ntInstructionDefinition); + bbCollapseLeaf.Enabled := + Assigned(NodeData) and (NodeData^.NodeType <> ntInstructionDefinition); +end; + +procedure TfrmMain.UpdateExpandedProperties; +var + I: Integer; +begin + if (Assigned(Inspector.InspectedObject)) then + begin + if (Inspector.InspectedObject is TInstructionFilter) then + begin + FExpandedFilterProperties.Clear; + end; + if (Inspector.InspectedObject is TInstructionDefinition) then + begin + FExpandedDefinitionProperties.Clear; + end; + for I := 0 to Inspector.Rows.Count - 1 do + begin + if (Inspector.Rows[I].Expanded) then + begin + if (Inspector.InspectedObject is TInstructionFilter) then + begin + FExpandedFilterProperties.Add( + (Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName); + end; + if (Inspector.InspectedObject is TInstructionDefinition) then + begin + FExpandedDefinitionProperties.Add( + (Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName); + end; + end; + end; + end; +end; + +procedure TfrmMain.UpdateStatistic; +var + Mnemonics: TDictionary; + Node: PVirtualNode; + NodeData: PEditorNodeData; +begin + if (not FUpdating) then + begin + Mnemonics := TDictionary.Create; + try + Node := EditorTree.GetFirst; + while Assigned(Node) do + begin + NodeData := EditorTree.GetNodeData(Node); + if (NodeData^.NodeType = ntInstructionDefinition) then + begin + if (not Mnemonics.ContainsKey(NodeData^.Definition.Mnemonic)) then + begin + Mnemonics.Add(NodeData^.Definition.Mnemonic, true); + end; + end; + Node := EditorTree.GetNext(Node); + end; + StatusBar.Panels[2].Text := 'Mnemonics: ' + IntToStr(Mnemonics.Count); + finally + Mnemonics.Free; + end; + StatusBar.Panels[3].Text := 'Definitions: ' + IntToStr(FEditor.DefinitionCount); + StatusBar.Panels[4].Text := 'Filters: ' + IntToStr(FEditor.FilterCount); + end; +end; + +procedure TfrmMain.InspectorItemChanged(Sender: TObject; AOldRow: TcxCustomRow; + AOldCellIndex: Integer); +var + Row: TcxPropertyRow; + S: String; +begin + lblPropertyInfo.Caption := 'No info text available'; + Row := (Inspector.FocusedRow as TcxPropertyRow); + if Assigned(Row) and Assigned(Row.PropertyEditor) then + begin + S := Row.PropertyEditor.GetName; + while (Assigned(Row.Parent)) do + begin + Row := (Row.Parent as TcxPropertyRow); + S := Row.PropertyEditor.GetName + '.' + S; + end; + if (Inspector.InspectedObject is TInstructionFilter) then + begin + S := 'Filter.' + S; + end else if (Inspector.InspectedObject is TInstructionDefinition) then + begin + S := 'Definition.' + S; + end; + lblPropertyInfo.Caption := GetPropertyHint(S); + end; +end; + +end. diff --git a/assets/InstructionEditor/InstructionEditor.dpr b/assets/InstructionEditor/InstructionEditor.dpr new file mode 100644 index 0000000..a0a8f88 --- /dev/null +++ b/assets/InstructionEditor/InstructionEditor.dpr @@ -0,0 +1,29 @@ +program InstructionEditor; + +uses + Vcl.Forms, + formMain in 'Forms\formMain.pas' {frmMain}, + untInstructionEditor in 'untInstructionEditor.pas', + untInstructionFilters in 'untInstructionFilters.pas', + untHelperClasses in 'untHelperClasses.pas', + formEditorCPUIDFeatureFlags in 'Forms\Editors\formEditorCPUIDFeatureFlags.pas' {frmEditorCPUIDFeatureFlags}, + Vcl.Themes, + Vcl.Styles, + untPropertyHints in 'untPropertyHints.pas', + formCreateDefinition in 'Forms\formCreateDefinition.pas' {frmCreateDefinition}, + untPropertyEditors in 'untPropertyEditors.pas', + formEditorX86Registers in 'Forms\Editors\formEditorX86Registers.pas' {frmEditorX86Registers}, + formGenerator in 'Forms\formGenerator.pas' {frmGenerator}, + formEditorInstructionOperands in 'Forms\Editors\formEditorInstructionOperands.pas' {frmEditorInstructionOperands}; + +{$R *.res} + +begin + {$IFDEF DEBUG} + ReportMemoryLeaksOnShutdown := true; + {$ENDIF} + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TfrmMain, frmMain); + Application.Run; +end. diff --git a/assets/InstructionEditor/InstructionEditor.dproj b/assets/InstructionEditor/InstructionEditor.dproj new file mode 100644 index 0000000..d780d2f --- /dev/null +++ b/assets/InstructionEditor/InstructionEditor.dproj @@ -0,0 +1,593 @@ + + + {33BB8B7D-4980-458D-8786-1256495555AE} + 18.1 + VCL + InstructionEditor.dpr + True + Debug + Win32 + 3 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + 1031 + $(BDS)\bin\default_app.manifest + CompanyName=zYantific.com;FileDescription=Instruction Definition Editor;FileVersion=1.0.0.0;InternalName=;LegalCopyright=Copyright © 2015 by zYantific.com;LegalTrademarks=;OriginalFilename=;ProductName=Instruction Definition Editor;ProductVersion=1.0.0.0;Comments= + InstructionEditor + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + true + InstructionEditor.ico + true + dxBarRS22;JvGlobus;JvMM;JvManagedThreads;dxSkinsdxRibbonPainterRS22;dxSkinOffice2010BlackRS22;FireDACPgDriver;dxPScxVGridLnkRS22;JvCrypt;DBXInterBaseDriver;DataSnapServer;DataSnapCommon;JvNet;dxWizardControlRS22;JvDotNetCtrls;DbxCommonDriver;vclimg;dbxcds;dxServerModeRS22;DatasnapConnectorsFreePascal;appanalytics;JvXPCtrls;vcldb;dxSkinDarkRoomRS22;dxSkinSilverRS22;dxGDIPlusRS22;CustomIPTransport;dxSkinOffice2010SilverRS22;dsnap;IndyIPServer;IndyCore;dxSkinSummer2008RS22;CloudService;dxSkinLondonLiquidSkyRS22;FireDACIBDriver;JvDB;JvRuntimeDesign;tmsdXE7;dxPScxPivotGridLnkRS22;dxComnRS22;cxPivotGridChartRS22;dsnapxml;dxSpellCheckerRS22;JclDeveloperTools;FireDACDb2Driver;dxPScxGridLnkRS22;dxPScxPCProdRS22;dxSkinVS2010RS22;dxorgcRS22;dxBarExtItemsRS22;dxRibbonRS22;dxSkinsdxBarPainterRS22;dxCoreRS22;cxVerticalGridRS22;bindcompfmx;dxSkinStardustRS22;dxSkinSevenClassicRS22;dxSkinsCoreRS22;dxSpreadSheetRS22;FireDACODBCDriver;RESTBackendComponents;cxSchedulerRS22;dbrtl;dxSkinDevExpressStyleRS22;FireDACCommon;bindcomp;inetdb;cxExportRS22;JvPluginSystem;dxSkinOffice2007BlackRS22;dxSkinXmas2008BlueRS22;dxSkinCoffeeRS22;DBXOdbcDriver;dxSkinscxSchedulerPainterRS22;ibmonitor;vclFireDAC;JvCmp;JvTimeFramework;xmlrtl;ibxpress;dxSkinOffice2013WhiteRS22;dxPSdxDBOCLnkRS22;cxDataRS22;FireDACCommonDriver;cxBarEditItemRS22;soaprtl;bindengine;vclactnband;bindcompvcl;dxBarExtDBItemsRS22;dxSkinOffice2010BlueRS22;Jcl;vclie;dxPSdxDBTVLnkRS22;cxPageControlRS22;dxPsPrVwAdvRS22;FireDACMSSQLDriver;DBXInformixDriver;dxSkinPumpkinRS22;dxSkinFoggyRS22;cxEditorsRS22;dxPScxExtCommonRS22;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;tmsexdXE7;inet;dxPSPrVwRibbonRS22;dxMapControlRS22;dxSkinSharpRS22;dxmdsRS22;JvPascalInterpreter;FireDACMySQLDriver;soapmidas;vclx;dxSkinOffice2007GreenRS22;dxSkinMetropolisDarkRS22;DBXSybaseASADriver;dxDockingRS22;RESTComponents;dxTabbedMDIRS22;dxdborRS22;dxSkinMoneyTwinsRS22;dbexpress;dxPScxTLLnkRS22;IndyIPClient;dxSkinSpringTimeRS22;FireDACTDataDriver;FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;dxSkinHighContrastRS22;dxSkinOffice2007SilverRS22;fmx;dxPSdxFCLnkRS22;JvDlgs;IndySystem;dxLayoutControlRS22;dxSkinOffice2007PinkRS22;tethering;cxSchedulerGridRS22;dxSkinMcSkinRS22;vclib;DataSnapClient;dxSkinsdxNavBarPainterRS22;dxNavBarRS22;DataSnapProviderClient;DBXSybaseASEDriver;dxSkinGlassOceansRS22;dxSkinBlueprintRS22;dxSkinBlueRS22;vcldsnap;dxBarDBNavRS22;dxSkiniMaginaryRS22;dxPSCoreRS22;dxSkinLiquidSkyRS22;fmxFireDAC;DBXDb2Driver;dxSkinOffice2007BlueRS22;dxSkinCaramelRS22;DBXOracleDriver;cxGridRS22;JvCore;vclribbon;fmxase;vcl;DBXMSSQLDriver;IndyIPCommon;dxThemeRS22;DataSnapFireDAC;FireDACDBXDriver;dxPScxSchedulerLnkRS22;JvAppFrm;soapserver;cxPivotGridRS22;dxtrmdRS22;dxSkinscxPCPainterRS22;inetdbxpress;FireDACInfxDriver;JvDocking;adortl;tmswizdXE7;cxLibraryRS22;dxPSdxSpreadSheetLnkRS22;JvWizards;dxFireDACServerModeRS22;dxPSdxLCLnkRS22;FireDACASADriver;dxSkinOffice2013DarkGrayRS22;JvHMI;dxPScxCommonRS22;JvBands;emsclientfiredac;rtl;DbxClientDriver;dxSkinOffice2013LightGrayRS22;JclContainers;dxFlowChartRS22;dxPSLnksRS22;dxSkinSharpPlusRS22;dxSkinDarkSideRS22;JvSystem;DataSnapNativeClient;svnui;cxTreeListdxBarPopupMenuRS22;JvControls;dxSkinsdxDLPainterRS22;dxSkinValentineRS22;IndyProtocols;DBXMySQLDriver;dxSkinDevExpressDarkStyleRS22;dxPSdxOCLnkRS22;dxSkinMetropolisRS22;bindcompdbx;JvJans;JvPrintPreview;JvPageComps;JvStdCtrls;JvCustom;cxTreeListRS22;dxSkinTheAsphaltWorldRS22;FireDACADSDriver;vcltouch;dxADOServerModeRS22;emsclient;dxdbtrRS22;VclSmp;FireDAC;VCLRESTComponents;dxTileControlRS22;cxSchedulerRibbonStyleEventEditorRS22;dxSkinSevenRS22;DataSnapConnectors;dxSkinBlackRS22;fmxobj;JclVcl;dxSkinWhiteprintRS22;tmsxlsdXE7;cxPivotGridOLAPRS22;svn;cxSchedulerTreeBrowserRS22;FireDACOracleDriver;fmxdae;VirtualTreesR;FireDACMSAccDriver;DataSnapIndy10ServerTransport;$(DCC_UsePackage) + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + true + true + InstructionEditor.ico + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + dxBarRS22;dxSkinsdxRibbonPainterRS22;dxSkinOffice2010BlackRS22;FireDACPgDriver;dxPScxVGridLnkRS22;DBXInterBaseDriver;DataSnapServer;DataSnapCommon;dxWizardControlRS22;DbxCommonDriver;vclimg;dbxcds;dxServerModeRS22;DatasnapConnectorsFreePascal;appanalytics;vcldb;dxSkinDarkRoomRS22;dxSkinSilverRS22;dxGDIPlusRS22;CustomIPTransport;dxSkinOffice2010SilverRS22;dsnap;IndyIPServer;IndyCore;dxSkinSummer2008RS22;CloudService;dxSkinLondonLiquidSkyRS22;FireDACIBDriver;tmsdXE7;dxPScxPivotGridLnkRS22;dxComnRS22;cxPivotGridChartRS22;dsnapxml;dxSpellCheckerRS22;FireDACDb2Driver;dxPScxGridLnkRS22;dxPScxPCProdRS22;dxSkinVS2010RS22;dxorgcRS22;dxBarExtItemsRS22;dxRibbonRS22;dxSkinsdxBarPainterRS22;dxCoreRS22;cxVerticalGridRS22;bindcompfmx;dxSkinStardustRS22;dxSkinSevenClassicRS22;dxSkinsCoreRS22;dxSpreadSheetRS22;FireDACODBCDriver;RESTBackendComponents;cxSchedulerRS22;dbrtl;dxSkinDevExpressStyleRS22;FireDACCommon;bindcomp;inetdb;cxExportRS22;dxSkinOffice2007BlackRS22;dxSkinXmas2008BlueRS22;dxSkinCoffeeRS22;DBXOdbcDriver;dxSkinscxSchedulerPainterRS22;ibmonitor;vclFireDAC;xmlrtl;ibxpress;dxSkinOffice2013WhiteRS22;dxPSdxDBOCLnkRS22;cxDataRS22;FireDACCommonDriver;cxBarEditItemRS22;soaprtl;bindengine;vclactnband;bindcompvcl;dxBarExtDBItemsRS22;dxSkinOffice2010BlueRS22;vclie;dxPSdxDBTVLnkRS22;cxPageControlRS22;dxPsPrVwAdvRS22;FireDACMSSQLDriver;DBXInformixDriver;dxSkinPumpkinRS22;dxSkinFoggyRS22;cxEditorsRS22;dxPScxExtCommonRS22;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;tmsexdXE7;inet;dxPSPrVwRibbonRS22;dxMapControlRS22;dxSkinSharpRS22;dxmdsRS22;FireDACMySQLDriver;soapmidas;vclx;dxSkinOffice2007GreenRS22;dxSkinMetropolisDarkRS22;DBXSybaseASADriver;dxDockingRS22;RESTComponents;dxTabbedMDIRS22;dxdborRS22;dxSkinMoneyTwinsRS22;dbexpress;dxPScxTLLnkRS22;IndyIPClient;dxSkinSpringTimeRS22;FireDACTDataDriver;FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;dxSkinHighContrastRS22;dxSkinOffice2007SilverRS22;fmx;dxPSdxFCLnkRS22;IndySystem;dxLayoutControlRS22;dxSkinOffice2007PinkRS22;tethering;cxSchedulerGridRS22;dxSkinMcSkinRS22;vclib;DataSnapClient;dxSkinsdxNavBarPainterRS22;dxNavBarRS22;DataSnapProviderClient;DBXSybaseASEDriver;dxSkinGlassOceansRS22;dxSkinBlueprintRS22;dxSkinBlueRS22;vcldsnap;dxBarDBNavRS22;dxSkiniMaginaryRS22;dxPSCoreRS22;dxSkinLiquidSkyRS22;fmxFireDAC;DBXDb2Driver;dxSkinOffice2007BlueRS22;dxSkinCaramelRS22;DBXOracleDriver;cxGridRS22;vclribbon;fmxase;vcl;DBXMSSQLDriver;IndyIPCommon;dxThemeRS22;DataSnapFireDAC;FireDACDBXDriver;dxPScxSchedulerLnkRS22;soapserver;cxPivotGridRS22;dxtrmdRS22;dxSkinscxPCPainterRS22;inetdbxpress;FireDACInfxDriver;adortl;cxLibraryRS22;dxPSdxSpreadSheetLnkRS22;dxFireDACServerModeRS22;dxPSdxLCLnkRS22;FireDACASADriver;dxSkinOffice2013DarkGrayRS22;dxPScxCommonRS22;emsclientfiredac;rtl;DbxClientDriver;dxSkinOffice2013LightGrayRS22;dxFlowChartRS22;dxPSLnksRS22;dxSkinSharpPlusRS22;dxSkinDarkSideRS22;DataSnapNativeClient;cxTreeListdxBarPopupMenuRS22;dxSkinsdxDLPainterRS22;dxSkinValentineRS22;IndyProtocols;DBXMySQLDriver;dxSkinDevExpressDarkStyleRS22;dxPSdxOCLnkRS22;dxSkinMetropolisRS22;bindcompdbx;cxTreeListRS22;dxSkinTheAsphaltWorldRS22;FireDACADSDriver;vcltouch;dxADOServerModeRS22;emsclient;dxdbtrRS22;VclSmp;FireDAC;VCLRESTComponents;dxTileControlRS22;cxSchedulerRibbonStyleEventEditorRS22;dxSkinSevenRS22;DataSnapConnectors;dxSkinBlackRS22;fmxobj;dxSkinWhiteprintRS22;cxPivotGridOLAPRS22;cxSchedulerTreeBrowserRS22;FireDACOracleDriver;fmxdae;VirtualTreesR;FireDACMSAccDriver;DataSnapIndy10ServerTransport;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + true + true + true + 1033 + false + + + DatabaseEditor.ico + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + false + true + 3 + true + true + 1033 + + + true + 1033 + true + + + + MainSource + + +
frmMain
+ dfm +
+ + + + +
frmEditorCPUIDFeatureFlags
+ dfm +
+ + +
frmCreateDefinition
+ dfm +
+ + +
frmEditorX86Registers
+ dfm +
+ +
frmGenerator
+ dfm +
+ +
frmEditorInstructionOperands
+ dfm +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + InstructionEditor.dpr + + + Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver + Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server + + + + + + InstructionEditor.exe + true + + + + + 0 + .dll;.bpl + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + + + Contents\Resources + 1 + + + + + classes + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + Contents\MacOS + 0 + + + 1 + + + Contents\MacOS + 1 + + + + + library\lib\mips + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 0 + + + 1 + + + Contents\MacOS + 1 + + + library\lib\armeabi-v7a + 1 + + + 1 + + + + + 0 + + + Contents\MacOS + 1 + .framework + + + + + 1 + + + 1 + + + 1 + + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + library\lib\armeabi + 1 + + + + + 0 + + + 1 + + + Contents\MacOS + 1 + + + + + 1 + + + 1 + + + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-large + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable-hdpi + 1 + + + + + Contents + 1 + + + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + res\values + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + res\drawable + 1 + + + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + 0 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 0 + .bpl + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\drawable-ldpi + 1 + + + + + 1 + + + 1 + + + + + + + + + + + + + True + True + + + 12 + + + + +
diff --git a/assets/InstructionEditor/InstructionEditor.ico b/assets/InstructionEditor/InstructionEditor.ico new file mode 100644 index 0000000000000000000000000000000000000000..608e7c9715a0ff3121eecd5cfd5fed317ed0d210 GIT binary patch literal 113473 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 literal 0 HcmV?d00001 diff --git a/assets/InstructionEditor/InstructionEditor.res b/assets/InstructionEditor/InstructionEditor.res new file mode 100644 index 0000000000000000000000000000000000000000..c485239d552990ff20857b6fcb872e70a9138243 GIT binary patch literal 115996 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%0AFf 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 this will change the TInvokeableVariantType.SetProperty() signature + {$define FPC_VARIANTSETVAR} + {$endif} + +{$else} + +{$ifdef DWSSCRIPT} // always defined since SMS 1.1.2 + {$define ISDWS} // e.g. for SmartMobileStudio or Delphi Web Script + {$define ISSMS} // for SmartMobileStudio +{$else} + {$ifdef NEXTGEN} + {$define HASINLINE} + {$ZEROBASEDSTRINGS OFF} // we expect to share code among platforms + {$define ISDELPHI2010} + {$define ISDELPHIXE} + {$define USETMONITOR} + {$if CompilerVersion >= 29.0} + {$define ISDELPHIXE8} // e.g. for System.Net.HttpClient + {$ifend} + {$else} + {$ifdef CONDITIONALEXPRESSIONS} // Delphi 6 or newer + {$ifdef UNICODE} + {$ifdef CPUX64} + {$define CPU64} + {$endif} + {$else} + {$define USEOBJECTINSTEADOFRECORD} + {$endif} + {$ifdef VER140} + {$define ISDELPHI6} + {$endif} + {$if CompilerVersion >= 18} // Delphi 2006 or newer + {$define HASINLINE} + {$ifend} + {$if CompilerVersion >= 21.0} + {$define ISDELPHI2010} + {$ifend} + {$if CompilerVersion >= 22.0} + {$define ISDELPHIXE} + {$ifend} + {$if CompilerVersion >= 23.0} + {$define ISDELPHIXE2} // e.g. for Vcl.Graphics + {$ifndef MSWINDOWS} + {$define USETMONITOR} + {$endif} + {$ifend} + {$if CompilerVersion >= 25.0} + {$define ISDELPHIXE4} + {$ZEROBASEDSTRINGS OFF} // we expect to share code among platforms + {$warn DUPLICATE_CTOR_DTOR OFF} // avoid W1029 unneeded hints + {$ifend} + {$if CompilerVersion >= 29.0} + {$define ISDELPHIXE8} // e.g. for System.Net.HttpClient + {$ifend} + {$else} + {$define ISDELPHI5OROLDER} + {$define USEOBJECTINSTEADOFRECORD} + {$endif CONDITIONALEXPRESSIONS} + {$endif NEXTGEN} + {$Q-} // disable overflow checking + {$R-} // disable range checking +{$endif DELPHIWEBSCRIPT} + +{$endif FPC} diff --git a/assets/InstructionEditor/SynCrossPlatformJSON.pas b/assets/InstructionEditor/SynCrossPlatformJSON.pas new file mode 100644 index 0000000..04fc928 --- /dev/null +++ b/assets/InstructionEditor/SynCrossPlatformJSON.pas @@ -0,0 +1,2150 @@ +/// minimum standand-alone cross-platform JSON process using variants +// - this unit is a part of the freeware Synopse mORMot framework, +// licensed under a MPL/GPL/LGPL tri-license; version 1.18 +unit SynCrossPlatformJSON; + +{ + This file is part of Synopse mORMot framework. + + Synopse mORMot framework. Copyright (C) 2016 Arnaud Bouchez + Synopse Informatique - http://synopse.info + + *** BEGIN LICENSE BLOCK ***** + Version: MPL 1.1/GPL 2.0/LGPL 2.1 + + The contents of this file are subject to the Mozilla Public License Version + 1.1 (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + for the specific language governing rights and limitations under the License. + + The Original Code is Synopse mORMot framework. + + The Initial Developer of the Original Code is Arnaud Bouchez. + + Portions created by the Initial Developer are Copyright (C) 2016 + the Initial Developer. All Rights Reserved. + + Contributor(s): + - Witya + + Alternatively, the contents of this file may be used under the terms of + either the GNU General Public License Version 2 or later (the "GPL"), or + the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + in which case the provisions of the GPL or the LGPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of either the GPL or the LGPL, and not to allow others to + use your version of this file under the terms of the MPL, indicate your + decision by deleting the provisions above and replace them with the notice + and other provisions required by the GPL or the LGPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the MPL, the GPL or the LGPL. + + ***** END LICENSE BLOCK ***** + + + Version 1.18 + - first public release, corresponding to mORMot Framework 1.18 + - would compile with Delphi for any platform (including NextGen for mobiles), + with FPC 2.7 or Kylix, and with SmartMobileStudio 2+ + - FPC prior to 2.7.1 has some issues with working with variants: UTF-8 + encoding is sometimes lost, and TInvokeableVariantType.SetProperty is broken + +} + +{$i SynCrossPlatform.inc} // define e.g. HASINLINE + +interface + +uses + SysUtils, + Classes, +{$ifdef NEXTGEN} + System.Generics.Collections, +{$else} + Contnrs, +{$endif} + Variants, + TypInfo; + +type + TStringDynArray = array of string; + TVariantDynArray = array of variant; + TIntegerDynArray = array of integer; + + /// this type is used to store BLOB content + TByteDynArray = array of byte; + PByteDynArray = ^TByteDynArray; + + {$ifndef UNICODE} + {$ifdef FPC} + NativeInt = PtrInt; + NativeUInt = PtrUInt; + {$else} + NativeInt = integer; + NativeUInt = cardinal; + {$endif} + RawByteString = AnsiString; + {$endif} + + // this type will store UTF-8 encoded buffer (also on NextGen platform) + {$ifdef NEXTGEN} + TUTF8Buffer = TBytes; + // TObjecTList is not defined in Mobile platforms + TObjectList = TObjectList; + {$else} + TUTF8Buffer = UTF8String; + {$endif} + + /// exception used during standand-alone cross-platform JSON process + EJSONException = class(Exception); + + /// which kind of document the TJSONVariantData contains + TJSONVariantKind = (jvUndefined, jvObject, jvArray); + + PJSONVariantData = ^TJSONVariantData; + + {$A-} + /// stores any JSON object or array as variant + // - this structure is not very optimized for speed or memory use, but is + // simple and strong enough for our client-side purpose + // - it is in fact already faster (and using less memory) than DBXJSON and + // SuperObject / XSuperObject libraries - of course, mORMot's TDocVariant + // is faster, as dwsJSON is in some cases, but those are not cross-platform + {$ifdef UNICODE} + TJSONVariantData = record + private + {$else} + TJSONVariantData = object + protected + {$endif} + VType: TVarType; + _Align: byte; + VKind: TJSONVariantKind; + VCount: integer; + function GetKind: TJSONVariantKind; + function GetCount: integer; + function GetVarData(const aName: string; var Dest: TVarData): boolean; + function GetValue(const aName: string): variant; + function GetValueCopy(const aName: string): variant; + procedure SetValue(const aName: string; const aValue: variant); + function GetItem(aIndex: integer): variant; + procedure SetItem(aIndex: integer; const aItem: variant); + public + /// names of this jvObject + Names: TStringDynArray; + /// values of this jvObject or jvArray + Values: TVariantDynArray; + /// initialize the low-level memory structure + procedure Init; overload; + /// initialize the low-level memory structure with a given JSON content + procedure Init(const JSON: string); overload; + /// initialize the low-level memory structure with a given array of variant + procedure InitFrom(const aValues: TVariantDynArray); overload; + /// access to a nested TJSONVariantData item + // - returns nil if aName was not found, or not a true TJSONVariantData item + function Data(const aName: string): PJSONVariantData; + {$ifdef HASINLINE}inline;{$endif} + /// access to a nested TJSONVariantData item, creating it if necessary + // - aPath can be specified with any depth, e.g. 'level1.level2.level3' + // - if the item does not exist or is not a true TJSONVariantData, a new + // one will be created, and returned as pointer + function EnsureData(const aPath: string): PJSONVariantData; + /// add a void TJSONVariantData to the jvArray and return a pointer to it + function AddItem: PJSONVariantData; + /// add a value to the jvArray + // - raise a ESJONException if the instance is a jvObject + procedure AddValue(const aValue: variant); + /// add a name/value pair to the jvObject + // - raise a ESJONException if the instance is a jvArray + procedure AddNameValue(const aName: string; const aValue: variant); + /// search for a name in this jvObject + function NameIndex(const aName: string): integer; + /// set a value of this jvObject to a given path + // - aPath can be specified with any depth, e.g. 'level1.level2.level3' + procedure SetPath(const aPath: string; const aValue: variant); + /// fill this document from a JSON array or object + function FromJSON(const JSON: string): boolean; + /// convert this document into JSON array or object + function ToJSON: string; + /// fill the published properties of supplied class from this JSON object + function ToObject(Instance: TObject): boolean; + /// create an instance, and fill its published properties from this JSON object + // - it should contain some "ClassName" properties, i.e. JSON should have + // been created by ObjectToJSON(Instance,true) and the class should have + // been registered with RegisterClassForJSON() + function ToNewObject: TObject; + /// kind of document this TJSONVariantData contains + // - returns jvUndefined if this instance is not a TJSONVariant custom variant + property Kind: TJSONVariantKind read GetKind; + /// number of items in this jvObject or jvArray + // - returns 0 if this instance is not a TJSONVariant custom variant + property Count: integer read GetCount; + /// access by name to a value of this jvObject + // - value is returned as (varVariant or varByRef) for best speed + // - will return UnAssigned if aName is not correct or this is not a jvObject + property Value[const aName: string]: variant read GetValue write SetValue; default; + /// access by name to a value of this jvObject + // - value is returned as a true copy (not varByRef) so this property is + // slower but safer than Value[], if the owning TJsonVariantData disappears + // - will return UnAssigned if aName is not correct or this is not a jvObject + property ValueCopy[const aName: string]: variant read GetValueCopy; + /// access by index to a value of this jvArray + // - will return UnAssigned if aIndex is not correct or this is not a jvArray + property Item[aIndex: integer]: variant read GetItem write SetItem; + end; + {$A+} + + /// low-level class used to register TJSONVariantData as custom type + // - allows late binding to values, e.g. + // ! jsonvar.avalue := jsonvar.avalue+1; + // - due to an issue with FPC implementation, you can only read properties, + // not set them, so you should write: + // ! TJSONVariantData(jsonvar)['avalue'] := jsonvar.avalue+1; + TJSONVariant = class(TInvokeableVariantType) + protected + {$ifndef FPC} + {$ifndef ISDELPHI6} + function FixupIdent(const AText: string): string; override; + {$endif} + {$endif} + public + procedure Copy(var Dest: TVarData; const Source: TVarData; + const Indirect: Boolean); override; + procedure Clear(var V: TVarData); override; + function GetProperty(var Dest: TVarData; const V: TVarData; + const Name: string): Boolean; override; + {$ifdef FPC_VARIANTSETVAR} // see http://mantis.freepascal.org/view.php?id=26773 + function SetProperty(var V: TVarData; const Name: string; + const Value: TVarData): Boolean; override; + {$else} + function SetProperty(const V: TVarData; const Name: string; + const Value: TVarData): Boolean; override; + {$endif} + procedure Cast(var Dest: TVarData; const Source: TVarData); override; + procedure CastTo(var Dest: TVarData; const Source: TVarData; + const AVarType: TVarType); override; + end; + + /// handle a JSON result table, as returned by mORMot's server + // - handle both expanded and non expanded layout + // - will be used e.g. on client side for variant-based ORM data parsing + TJSONTable = class + protected + fJSON: string; + fFieldNames: TStringDynArray; + fJSONExpanded: boolean; + fJSONIndexFirstValue: integer; + fJSONCurrentIndex: integer; + fRowValues: TVariantDynArray; + function Get(const FieldName: string): variant; + public + /// parse the supplied JSON content + constructor Create(const aJSON: string); + /// case-insensitive search for a field name + function FieldIndex(const FieldName: string): integer; + /// to be called in a loop to iterate through all data rows + // - if returned true, Value[] contains the fields of this row + function Step(SeekFirst: boolean=false): boolean; + /// to be called in a loop to iterate through all data rows + // - if returned true, RowValues contains this row as TJSONVariant + function StepValue(var RowValues: variant; SeekFirst: boolean=false): boolean; + /// after Step() returned true, can be used to retrieve a field value by name + property Value[const FieldName: string]: variant read Get; default; + /// after Step() returned true, can be used to retrieve a field value by index + property RowValues: TVariantDynArray read fRowValues; + /// the recognized field names + property FieldNames: TStringDynArray read fFieldNames; + /// the associated JSON content + property JSON: string read fJSON; + end; + + /// an abstract type used for RTTI type information + TRTTITypeInfo = PPropInfo; + + /// an abstract type used for RTTI property information + TRTTIPropInfo = PPropInfo; + + TRTTIPropInfoDynArray = array of TRTTIPropInfo; + + /// handle a JSON result table, as returned by mORMot's server + // - handle both expanded and non expanded layout + // - this class is able to use RTTI to fill all published properties of + // a TObject + TJSONTableObject = class(TJSONTable) + protected + fTypeInfo: pointer; + fPropInfo: array of TRTTIPropInfo; + procedure FillPropInfo(aTypeInfo: TRTTITypeInfo); virtual; + procedure FillInstance(Instance: TObject); virtual; + function GetPropInfo(aTypeInfo: TRTTITypeInfo; const PropName: string): TRTTIPropInfo; virtual; + public + /// to be called in a loop to iterate through all data rows + // - if returned true, Object published properties will contain this row + function StepObject(Instance: TObject; SeekFirst: boolean=false): boolean; virtual; + end; + + /// used e.g. by TSynTest for each test case + TPublishedMethod = record + Name: string; + Method: TMethod; + end; + /// as filled by GetPublishedMethods() + TPublishedMethodDynArray = array of TPublishedMethod; + + +/// create a TJSONVariant instance from a given JSON content +// - typical usage may be: +//! var doc: variant; +//! json: string; +//! begin +//! doc := JSONVariant('{"test":1234,"name":"Joh\"n\r"}'); +//! assert(doc.test=1234); // access via late binding +//! assert(doc.name='Joh"n'#13); +//! assert(doc.name2=null); // unknown properties returns null +//! json := doc; // to convert a TJSONVariant to JSON, just assign to a string +//! assert(json='{"test":1234,"name":"Joh\"n\r"}'); +//! end; +// - note that FPC does not allow to set values by late-binding +function JSONVariant(const JSON: string): variant; overload; + +/// create a TJSONVariant TJSONVariant array from a supplied array of values +function JSONVariant(const values: TVariantDynArray): variant; overload; + +/// create a TJSONVariant TJSONVariant array from a supplied array of values +function JSONVariantFromConst(const constValues: array of variant): variant; + +/// access to a TJSONVariant instance members +// - e.g. Kind, Count, Names[] or Values[] +// - will raise an exception if the supplied variant is not a TJSONVariant +// - this function is safer than TJSONVariant(JSONVariant) +function JSONVariantData(const JSONVariant: variant): PJSONVariantData; + +/// access to a TJSONVariant instance members +// - e.g. Kind, Count, Names[] or Values[] +// - will return a read-only fake TJSONVariant with Kind=jvUndefined if the +// supplied variant is not a TJSONVariant +// - if ExpectedKind is jvArray of jvObject, it would return a fake TJSONVariant +// with Kind=jvUndefined if the JSONVariant kind does not match - so you can write: +// !var _a: integer; +// ! _arr: PJSONVariantData; +// !... +// ! _arr := JSONVariantDataSafe(_variant,jvArray); +// ! SetLength(result,_arr.Count); +// ! for _a := 0 to _arr.Count-1 do +// ! result[_a] := _arr.Values[_a]; +// in the above code, _arr.Count will be 0 if _variant.Kind<>jvArray +// - this function is safer than TJSONVariant(JSONVariant) +function JSONVariantDataSafe(const JSONVariant: variant; + ExpectedKind: TJSONVariantKind=jvUndefined): PJSONVariantData; + +var + /// the custom variant type definition registered for TJSONVariant + JSONVariantType: TInvokeableVariantType; + + +/// compute the quoted JSON string corresponding to the supplied text +function StringToJSON(const Text: string): string; + +/// compute the JSON representation of a floating-point value +procedure DoubleToJSON(Value: double; var result: string); + +/// compute the ISO-8601 JSON text representation of a date/time value +// - e.g. "YYYY-MM-DD" "Thh:mm:ss" or "YYYY-MM-DDThh:mm:ss" +// - if Date is 0, will return "" +function DateTimeToJSON(Value: TDateTime): string; + +/// compute the JSON representation of a variant value +// - will work for simple types, or TJSONVariant object or array +function ValueToJSON(const Value: variant): string; + +/// compute a variant from its JSON representation +// - will work for simple types, or TJSONVariant object or array +function JSONToValue(const JSON: string): variant; + +/// compute the ISO-8601 JSON text representation of the current date/time value +// - e.g. "2015-06-27T20:59:29" +function NowToIso8601: string; + +/// compute the unquoted ISO-8601 text representation of a date/time value +// - e.g. 'YYYY-MM-DD' 'Thh:mm:ss' or 'YYYY-MM-DDThh:mm:ss' +// - if Date is 0, will return '' +function DateTimeToIso8601(Value: TDateTime): string; + +/// convert unquoted ISO-8601 text representation into a date/time value +// - e.g. 'YYYY-MM-DD' 'Thh:mm:ss' or 'YYYY-MM-DDThh:mm:ss' +function Iso8601ToDateTime(const Value: string): TDateTime; + +/// compute the JSON representation of an object published properties +// - handle only simple types of properties, not nested class instances +// - any TList/TObjectList/TCollection will be serialized as JSON array +function ObjectToJSON(Instance: TObject; StoreClassName: boolean=false): string; + +/// fill an object published properties from the supplied JSON object +// - handle only simple types of properties, not nested class instances +function JSONToObject(Instance: TObject; const JSON: string): boolean; + +/// create a new object and fil its published properties from the supplied +// JSON object, which should include "ClassName":"..." properties +// - JSON should have been created with ObjectToJSON(Instance,true) and +// the class should have been registered with RegisterClassForJSON() +function JSONToNewObject(const JSON: string): pointer; + +/// register the class types to be created from its name +// - used e.g. by JSONToNewObject() or TJSONVariantData.ToNewObject +procedure RegisterClassForJSON(const Classes: array of TClass); + +/// create a class instance from its name +// - the class should have been registered previously via RegisterClassForJSON() +// - if the supplied class name is not found, will return nil +function CreateClassForJSON(const ClassName: string): TObject; + +/// create a list of object published properties from the supplied JSON object +// - handle only simple types of properties, not nested class instances +function JSONToObjectList(ItemClass: TClass; const JSON: string): TObjectList; + +/// return a string corresponding to the type name, as stored in the RTTI +// - e.g. 'TDateTime', 'TByteDynArray', 'TModTime', 'TCreateTime' +function RTTIPropInfoTypeName(PropInfo: TRTTIPropInfo): string; + +/// retrieve the published properties type information about a given class +procedure GetPropsInfo(TypeInfo: TRTTITypeInfo; var PropNames: TStringDynArray; + var PropRTTI: TRTTIPropInfoDynArray); + +/// retrieve the value of a published property as variant +function GetInstanceProp(Instance: TObject; PropInfo: TRTTIPropInfo): variant; + +/// set the value of a published property from a variant +procedure SetInstanceProp(Instance: TObject; PropInfo: TRTTIPropInfo; + const Value: variant); + +/// retrieve all the published methods of a given class, using RTTI +procedure GetPublishedMethods(Instance: TObject; + out Methods: TPublishedMethodDynArray); + +/// convert an "array of const" parameter value into its string representation +function VarRecToValue(const V: TVarRec; out wasString: boolean): string; + +/// convert the supplied text as "text", as expected by SQL standard +procedure DoubleQuoteStr(var text: string); + +/// decode a Base64-encoded string +// - default withBase64Magic=TRUE will expect the string to start with our +// JSON_BASE64_MAGIC marker +function Base64JSONStringToBytes(const JSONString: string; + var Bytes: TByteDynArray; withBase64Magic: boolean=true): boolean; + +/// Base-64 encode a BLOB into string +// - default withBase64Magic=TRUE will include our JSON_BASE64_MAGIC marker +function BytesToBase64JSONString(const Bytes: TByteDynArray; + withBase64Magic: boolean=true): string; + +const + /// special code to mark Base64 binary content in JSON string + // - Unicode special char U+FFF0 is UTF-8 encoded as EF BF B0 bytes + // - prior to Delphi 2009, it won't work as expected since U+FFF0 won't be + // able to be converted into U+FFF0 + {$ifdef UNICODE} + JSON_BASE64_MAGIC: word = $fff0; + {$else} + JSON_BASE64_MAGIC: array[0..2] of byte = ($ef,$bf,$b0); + {$endif} + + /// size, in platform chars, of our special code to mark Base64 binary + // content in JSON string + // - equals 1 since Delphi 2009 (UTF-16 encoded), or 3 for older versions + // (UTF-8encoded) of the compiler compiler + JSON_BASE64_MAGIC_LEN = sizeof(JSON_BASE64_MAGIC) div sizeof(char); + +{$ifndef ISSMS} +/// read an UTF-8 (JSON) file into a native string +// - file should be existing, otherwise an exception is raised +function UTF8FileToString(const aFileName: TFileName): string; +{$endif} + +/// this function is faster than str := str+chr ! +procedure AppendChar(var str: string; chr: Char); + {$ifdef HASINLINE}inline;{$endif} + +/// check that two ASCII-7 latin text do match +function IdemPropName(const PropName1,PropName2: string): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// check that two ASCII-7 latin text do match +// - first parameter is expected to be a shortstring low-level buffer - as such, +// this overloaded function would work with NEXTGEN encoded RTTI +function IdemPropName(PropName1: PByteArray; const PropName2: string): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// convert ASCII-7 latin text, encoded as a shortstring buffer, into a string +// - as such, this function would work with NEXTGEN encoded RTTI +function ShortStringToString(Buffer: PByteArray): string; + +/// check that two ASCII-7 latin text do match +function StartWithPropName(const PropName1,PropName2: string): boolean; + + +implementation + +function IdemPropName(const PropName1,PropName2: string): boolean; +var L,i: integer; +begin + result := false; + L := length(PropName2); + if length(PropName1)<>L then + exit; + for i := 1 to L do + if (ord(PropName1[i]) xor ord(PropName2[i])) and + {$ifdef UNICODE}$ffdf{$else}$df{$endif}<>0 then + exit; + result := true; +end; + +function ShortStringToString(Buffer: PByteArray): string; +{$ifdef UNICODE} +var i: integer; +begin + SetLength(result,Buffer^[0]); + for i := 1 to Buffer^[0] do + result[i] := chr(Buffer^[i]); +end; +{$else} +begin + SetString(result,PAnsiChar(@Buffer^[1]),Buffer^[0]); +end; +{$endif} + +function IdemPropName(PropName1: PByteArray; const PropName2: string): boolean; +var L,i: integer; +begin + result := false; + L := length(PropName2); + if PropName1^[0]<>L then + exit; + for i := 1 to L do + if (PropName1^[i] xor ord(PropName2[i])) and + {$ifdef UNICODE}$ffdf{$else}$df{$endif}<>0 then + exit; + result := true; +end; + +function StartWithPropName(const PropName1,PropName2: string): boolean; +var L,i: integer; +begin + result := false; + L := length(PropName2); + if length(PropName1)0 then + exit; + result := true; +end; + +{$ifndef ISSMS} // there is no file within HTML5 DOM + +{$ifdef FPC} +// assume string is UTF-8 encoded (as with Lazarus/LCL) +// note that when working with variants, FPC 2.7.1 sometimes clear the code page +type UTF8ToString = RawByteString; +{$else} +{$ifndef UNICODE} +function UTF8ToString(const utf8: TUTF8Buffer): string; +begin + result := UTF8ToAnsi(utf8); +end; +{$endif} +{$endif} + +function UTF8FileToString(const aFileName: TFileName): string; +var F: TFileStream; + len: integer; + utf8: TUTF8Buffer; +begin + F := TFileStream.Create(aFileName,fmOpenRead); + try + len := F.Size; + SetLength(utf8,len); + {$ifdef NEXTGEN} + F.Read(utf8[0],len); + result := TEncoding.UTF8.GetString(utf8); + {$else} + F.Read(utf8[1],len); + result := UTF8ToString(utf8); + {$endif} + finally + F.Free; + end; +end; +{$endif} + +function JSONVariant(const JSON: string): variant; +begin + VarClear(result); + TJSONVariantData(result).FromJSON(JSON); +end; + +function JSONVariant(const values: TVariantDynArray): variant; +begin + VarClear(result); + TJSONVariantData(result).Init; + TJSONVariantData(result).VKind := jvArray; + TJSONVariantData(result).VCount := length(values); + TJSONVariantData(result).Values := values; +end; + +function JSONVariantFromConst(const constValues: array of variant): variant; +var i: integer; +begin + VarClear(result); + with TJSONVariantData(result) do begin + Init; + VKind := jvArray; + VCount := length(values); + SetLength(Values,VCount); + for i := 0 to VCount-1 do + Values[i] := constValues[i]; + end; +end; + +function JSONVariantData(const JSONVariant: variant): PJSONVariantData; +begin + with TVarData(JSONVariant) do + if VType=JSONVariantType.VarType then + result := @JSONVariant else + if VType=varByRef or varVariant then + result := JSONVariantData(PVariant(VPointer)^) else + raise EJSONException.CreateFmt('JSONVariantData.Data(%d<>JSONVariant)',[VType]); +end; + +const // will be in code section of the exe, so will be read-only by design + JSONVariantDataFake: TJSONVariantData = (); + +function JSONVariantDataSafe(const JSONVariant: variant; + ExpectedKind: TJSONVariantKind=jvUndefined): PJSONVariantData; +begin + with TVarData(JSONVariant) do + if VType=JSONVariantType.VarType then + if (ExpectedKind=jvUndefined) or + (TJSONVariantData(JSONVariant).VKind=ExpectedKind) then + result := @JSONVariant else + result := @JSONVariantDataFake else + if VType=varByRef or varVariant then + result := JSONVariantDataSafe(PVariant(VPointer)^) else + result := @JSONVariantDataFake; +end; + +procedure AppendChar(var str: string; chr: Char); +{$ifdef ISSMS} // JavaScript immutable strings +begin + str := str+chr +end; +{$else} +var len: Integer; +begin + len := length(str); + SetLength(str,len+1); + PChar(pointer(str))[len] := chr; // SetLength() made str unique +end; +{$endif} + +function StringToJSON(const Text: string): string; +var len,j: integer; +procedure DoEscape; +var i: Integer; +begin + result := '"'+copy(Text,1,j-1); // here FPC 2.7.1 erases UTF-8 encoding + for i := j to len do begin + case Text[i] of + #8: result := result+'\b'; + #9: result := result+'\t'; + #10: result := result+'\n'; + #12: result := result+'\f'; + #13: result := result+'\r'; + '\': result := result+'\\'; + '"': result := result+'\"'; + else + if Text[i]<' ' then + result := result+'\u00'+IntToHex(ord(Text[i]),2) else + AppendChar(result,Text[i]); // will be UTF-8 encoded later + end; + end; + AppendChar(result,'"'); +end; +begin + len := length(Text); + for j := 1 to len do + case Text[j] of + #0..#31,'\','"': begin + DoEscape; + exit; + end; + end; + // if we reached here, no character needs to be escaped in this string + result := '"'+Text+'"'; // here FPC 2.7.1 erases UTF-8 encoding :( +end; + +{$ifdef KYLIX} + {$define NOFORMATSETTINGS} +{$endif} +{$ifdef ISDELPHI6} + {$define NOFORMATSETTINGS} +{$endif} + +{$ifdef NOFORMATSETTINGS} +procedure DoubleToJSON(Value: double; var result: string); +var decsep: Char; +begin // warning: this is NOT thread-safe if you mix settings + decsep := DecimalSeparator; + result := FloatToStr(Value); + DecimalSeparator := decsep; +end; +{$else} +var + SettingsUS: TFormatSettings + {$ifdef FPC} = ( + CurrencyFormat: 1; + NegCurrFormat: 5; + ThousandSeparator: ','; + DecimalSeparator: '.'; + CurrencyDecimals: 2; + DateSeparator: '-'; + TimeSeparator: ':'; + ListSeparator: ','; + CurrencyString: '$'; + ShortDateFormat: 'd/m/y'; + LongDateFormat: 'dd" "mmmm" "yyyy'; + TimeAMString: 'AM'; + TimePMString: 'PM'; + ShortTimeFormat: 'hh:nn'; + LongTimeFormat: 'hh:nn:ss'; + ShortMonthNames: ('Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'); + LongMonthNames: ('January','February','March','April','May','June', + 'July','August','September','October','November','December'); + ShortDayNames: ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); + LongDayNames: ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); + TwoDigitYearCenturyWindow: 50;) + {$endif}; +procedure DoubleToJSON(Value: double; var result: string); +begin + result := FloatToStr(Value,SettingsUS); +end; +{$endif} + +function DateTimeToJSON(Value: TDateTime): string; +begin // e.g. "YYYY-MM-DD" "Thh:mm:ss" or "YYYY-MM-DDThh:mm:ss" + result := '"'+DateTimeToIso8601(Value)+'"'; +end; + +function NowToIso8601: string; +begin + result := DateTimeToIso8601(Now); +end; + +function DateTimeToIso8601(Value: TDateTime): string; +begin // e.g. YYYY-MM-DD Thh:mm:ss or YYYY-MM-DDThh:mm:ss + if Value=0 then + result := '' else + if frac(Value)=0 then + result := FormatDateTime('yyyy"-"mm"-"dd',Value) else + if trunc(Value)=0 then + result := FormatDateTime('"T"hh":"nn":"ss',Value) else + result := FormatDateTime('yyyy"-"mm"-"dd"T"hh":"nn":"ss',Value); +end; + +function Iso8601ToDateTime(const Value: string): TDateTime; +var Y,M,D, HH,MI,SS: cardinal; +begin // YYYY-MM-DD Thh:mm:ss or YYYY-MM-DDThh:mm:ss + // 1234567890 123456789 1234567890123456789 + result := 0; + case Length(Value) of + 9: if (Value[1]='T') and (Value[4]=':') and (Value[7]=':') then begin + HH := ord(Value[2])*10+ord(Value[3])-(48+480); + MI := ord(Value[5])*10+ord(Value[6])-(48+480); + SS := ord(Value[8])*10+ord(Value[9])-(48+480); + if (HH<24) and (MI<60) and (SS<60) then + result := EncodeTime(HH,MI,SS,0); + end; + 10: if (Value[5]=Value[8]) and (ord(Value[8]) in [ord('-'),ord('/')]) then begin + Y := ord(Value[1])*1000+ord(Value[2])*100+ + ord(Value[3])*10+ord(Value[4])-(48+480+4800+48000); + M := ord(Value[6])*10+ord(Value[7])-(48+480); + D := ord(Value[9])*10+ord(Value[10])-(48+480); + if (Y<=9999) and ((M-1)<12) and ((D-1)<31) then + result := EncodeDate(Y,M,D); + end; + 19: if (Value[5]=Value[8]) and (ord(Value[8]) in [ord('-'),ord('/')]) and + (ord(Value[11]) in [ord(' '),ord('T')]) and (Value[14]=':') and (Value[17]=':') then begin + Y := ord(Value[1])*1000+ord(Value[2])*100+ + ord(Value[3])*10+ord(Value[4])-(48+480+4800+48000); + M := ord(Value[6])*10+ord(Value[7])-(48+480); + D := ord(Value[9])*10+ord(Value[10])-(48+480); + HH := ord(Value[12])*10+ord(Value[13])-(48+480); + MI := ord(Value[15])*10+ord(Value[16])-(48+480); + SS := ord(Value[18])*10+ord(Value[19])-(48+480); + if (Y<=9999) and ((M-1)<12) and ((D-1)<31) and + (HH<24) and (MI<60) and (SS<60) then + result := EncodeDate(Y,M,D)+EncodeTime(HH,MI,SS,0); + end; + end; +end; + +function ValueToJSON(const Value: variant): string; +var I64: Int64; +begin + if TVarData(Value).VType=JSONVariantType.VarType then + result := TJSONVariantData(Value).ToJSON else + if (TVarData(Value).VType=varByRef or varVariant) then + result := ValueToJSON(PVariant(TVarData(Value).VPointer)^) else + if TVarData(Value).VType<=varNull then + result := 'null' else + if VarIsOrdinal(Value) then begin + I64 := Value; + result := IntToStr(I64); + end else + if TVarData(Value).VType=varDate then + result := DateTimeToJSON(TVarData(Value).VDouble) else + if VarIsFloat(Value) then + DoubleToJSON(Value,result) else + if VarIsStr(Value) then + result := StringToJSON(Value) else + result := Value; +end; + +function VarRecToValue(const V: TVarRec; out wasString: boolean): string; +// http://smartmobilestudio.com/forums/topic/is-array-of-const-supported-in-sms +begin + wasString := not (V.VType in + [vtBoolean,vtInteger,vtInt64,vtCurrency,vtExtended,vtVariant]); + with V do + case VType of + {$ifndef NEXTGEN} + vtString: result := string(VString^); + vtAnsiString: result := string(AnsiString(VAnsiString)); + vtChar: result := string(VChar); + vtPChar: result := string(VPChar); + vtWideString: result := string(WideString(VWideString)); + {$endif} + {$ifdef UNICODE} + vtUnicodeString: result := string(VUnicodeString); + {$endif} + vtPWideChar: result := string(VPWideChar); + vtWideChar: result := string(VWideChar); + vtBoolean: if VBoolean then result := '1' else result := '0'; + vtInteger: result := IntToStr(VInteger); + vtInt64: result := IntToStr(VInt64^); + vtCurrency: DoubleToJSON(VCurrency^,result); + vtExtended: DoubleToJSON(VExtended^,result); + vtObject: result := ObjectToJSON(VObject); + vtVariant: if TVarData(VVariant^).VType<=varNull then + result := 'null' else begin + wasString := VarIsStr(VVariant^); + result := VVariant^; + end; + else result := ''; + end; +end; + +procedure DoubleQuoteStr(var text: string); +var i,j: integer; + tmp: string; +begin + i := pos('"',text); + if i=0 then begin + text := '"'+text+'"'; + exit; + end; + tmp := '"'+copy(text,1,i)+'"'; + for j := i+1 to length(text) do + if text[j]='"' then + tmp := tmp+'""' else + AppendChar(tmp,text[j]); + text := tmp+'"'; +end; + + +{ TJSONParser } + +type + /// the JSON node types, as recognized by TJSONParser + TJSONParserKind = ( + kNone, kNull, kFalse, kTrue, kString, kInteger, kFloat, kObject, kArray); + + /// used to parse any JSON content + TJSONParser = {$ifdef UNICODE}record{$else}object{$endif} + JSON: string; + Index: integer; + JSONLength: integer; + procedure Init(const aJSON: string; aIndex: integer); + function GetNextChar: char; {$ifdef HASINLINE}inline;{$endif} + function GetNextNonWhiteChar: char; {$ifdef HASINLINE}inline;{$endif} + function CheckNextNonWhiteChar(aChar: char): boolean; {$ifdef HASINLINE}inline;{$endif} + function GetNextString(out str: string): boolean; overload; + function GetNextString: string; overload; {$ifdef HASINLINE}inline;{$endif} + function GetNextJSON(out Value: variant): TJSONParserKind; + function CheckNextIdent(const ExpectedIdent: string): Boolean; + function GetNextAlphaPropName(out fieldName: string): boolean; + function ParseJSONObject(var Data: TJSONVariantData): boolean; + function ParseJSONArray(var Data: TJSONVariantData): boolean; + procedure GetNextStringUnEscape(var str: string); + end; + +procedure TJSONParser.Init(const aJSON: string; aIndex: integer); +begin + JSON := aJSON; + JSONLength := length(JSON); + Index := aIndex; +end; + +function TJSONParser.GetNextChar: char; +begin + if Index<=JSONLength then begin + result := JSON[Index]; + inc(Index); + end else + result := #0; +end; + +function TJSONParser.GetNextNonWhiteChar: char; +begin + if Index<=JSONLength then + repeat + if JSON[Index]>' ' then begin + result := JSON[Index]; + inc(Index); + exit; + end; + inc(Index); + until Index>JSONLength; + result := #0; +end; + +function TJSONParser.CheckNextNonWhiteChar(aChar: char): boolean; +begin + if Index<=JSONLength then + repeat + if JSON[Index]>' ' then begin + result := JSON[Index]=aChar; + if result then + inc(Index); + exit; + end; + inc(Index); + until Index>JSONLength; + result := false; +end; + +procedure TJSONParser.GetNextStringUnEscape(var str: string); +var c: char; + u: string; + unicode,err: integer; +begin + repeat + c := GetNextChar; + case c of + #0: exit; + '"': break; + '\': begin + c := GetNextChar; + case c of + #0: exit; + 'b': AppendChar(str,#08); + 't': AppendChar(str,#09); + 'n': AppendChar(str,#$0a); + 'f': AppendChar(str,#$0c); + 'r': AppendChar(str,#$0d); + 'u': begin + u := Copy(JSON,Index,4); + if length(u)<>4 then + exit; + inc(Index,4); + val('$'+u,unicode,err); + if err<>0 then + exit; + AppendChar(str,char(unicode)); + end; + else AppendChar(str,c); + end; + end; + else AppendChar(str,c); + end; + until false; +end; + +function TJSONParser.GetNextString(out str: string): boolean; +var i: integer; +begin + for i := Index to JSONLength do + case JSON[i] of + '"': begin // end of string without escape -> direct copy + str := copy(JSON,Index,i-Index); + Index := i+1; + result := true; + exit; + end; + '\': begin // need unescaping + str := copy(JSON,Index,i-Index); + Index := i; + GetNextStringUnEscape(str); + result := true; + exit; + end; + end; + result := false; +end; + +function TJSONParser.GetNextString: string; +begin + if not GetNextString(result) then + result := ''; +end; + +function TJSONParser.GetNextAlphaPropName(out fieldName: string): boolean; +var i: integer; +begin + result := False; + if (Index>=JSONLength) or + not (Ord(JSON[Index]) in [Ord('A')..Ord('Z'),Ord('a')..Ord('z'),Ord('_'),Ord('$')]) then + exit; // first char must be alphabetical + for i := Index+1 to JSONLength do + case Ord(JSON[i]) of + Ord('0')..Ord('9'),Ord('A')..Ord('Z'),Ord('a')..Ord('z'),Ord('_'): + ; // allow MongoDB extended syntax, e.g. {age:{$gt:18}} + Ord(':'),Ord('='): begin // allow both age:18 and age=18 pairs + fieldName := Copy(JSON,Index,i-Index); + Index := i+1; + result := true; + exit; + end; + else exit; + end; +end; + +function TJSONParser.GetNextJSON(out Value: variant): TJSONParserKind; +var str: string; + i64: Int64; + d: double; + start,err: integer; +begin + result := kNone; + case GetNextNonWhiteChar of + 'n': if copy(JSON,Index,3)='ull' then begin + inc(Index,3); + result := kNull; + Value := null; + end; + 'f': if copy(JSON,Index,4)='alse' then begin + inc(Index,4); + result := kFalse; + Value := false; + end; + 't': if copy(JSON,Index,3)='rue' then begin + inc(Index,3); + result := kTrue; + Value := true; + end; + '"': if GetNextString(str) then begin + result := kString; + Value := str; + end; + '{': if ParseJSONObject(TJSONVariantData(Value)) then + result := kObject; + '[': if ParseJSONArray(TJSONVariantData(Value)) then + result := kArray; + '-','0'..'9': begin + start := Index-1; + while true do + case JSON[Index] of + '-','+','0'..'9','.','E','e': inc(Index); + else break; + end; + str := copy(JSON,start,Index-start); + val(str,i64,err); + if err=0 then begin + Value := i64; + result := kInteger; + end else begin + val(str,d,err); + if err<>0 then + exit; + Value := d; + result := kFloat; + end; + end; + end; +end; + +function TJSONParser.CheckNextIdent(const ExpectedIdent: string): Boolean; +begin + result := (GetNextNonWhiteChar='"') and + (CompareText(GetNextString,ExpectedIdent)=0) and + (GetNextNonWhiteChar=':'); +end; + +function TJSONParser.ParseJSONArray(var Data: TJSONVariantData): boolean; +var item: variant; +begin + result := false; + Data.Init; + if not CheckNextNonWhiteChar(']') then // '[]' -> void array + repeat + if GetNextJSON(item)=kNone then + exit; + Data.AddValue(item); + case GetNextNonWhiteChar of + ',': continue; + ']': break; + else exit; + end; + until false; + SetLength(Data.Values,Data.VCount); + Data.VKind := jvArray; + result := true; +end; + +function TJSONParser.ParseJSONObject(var Data: TJSONVariantData): boolean; +var key: string; + val: variant; +begin + result := false; + Data.Init; + if not CheckNextNonWhiteChar('}') then // '{}' -> void object + repeat + if CheckNextNonWhiteChar('"') then begin + if (not GetNextString(key)) or (GetNextNonWhiteChar<>':') then + exit; + end else + if not GetNextAlphaPropName(key) then + exit; + if GetNextJSON(val)=kNone then + exit; // writeln(Copy(JSON,Index-10,30)); + Data.AddNameValue(key,val); + case GetNextNonWhiteChar of + ',': continue; + '}': break; + else exit; + end; + until false; + SetLength(Data.Names,Data.VCount); + SetLength(Data.Values,Data.VCount); + Data.VKind := jvObject; + result := true; +end; + + +function JSONToValue(const JSON: string): variant; +var Parser: TJSONParser; +begin + Parser.Init(JSON,1); + Parser.GetNextJSON(result); +end; + + +{ RTTI-oriented functions } + +const + BASE64: array[0..63] of char = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var + BASE64DECODE: array of ShortInt; + +function BytesToBase64JSONString(const Bytes: TByteDynArray; + withBase64Magic: boolean): string; +var i,len,x,c,j: cardinal; + P: PChar; +begin + len := length(Bytes); + if len=0 then begin + result := ''; + exit; + end; + if withBase64Magic then + x := JSON_BASE64_MAGIC_LEN else + x := 0; + SetLength(result,((len+2)div 3)*4+x); + P := pointer(result); + if withBase64Magic then + move(JSON_BASE64_MAGIC,P^,sizeof(JSON_BASE64_MAGIC)); + j := 0; + for i := 1 to len div 3 do begin + c := Bytes[j] shl 16 or Bytes[j+1] shl 8 or Bytes[j+2]; + inc(j,3); + P[x] := BASE64[(c shr 18) and $3f]; + P[x+1] := BASE64[(c shr 12) and $3f]; + P[x+2] := BASE64[(c shr 6) and $3f]; + P[x+3] := BASE64[c and $3f]; + inc(x,4); + end; + case len mod 3 of + 1: begin + c := Bytes[j] shl 4; + P[x] := BASE64[(c shr 6) and $3f]; + P[x+1] := BASE64[c and $3f]; + P[x+2] := '='; + P[x+3] := '='; + inc(x,4); + end; + 2: begin + c := Bytes[j] shl 10 or Bytes[j+1] shl 2; + P[x] := BASE64[(c shr 12) and $3f]; + P[x+1] := BASE64[(c shr 6) and $3f]; + P[x+2] := BASE64[c and $3f]; + P[x+3] := '='; + inc(x,4); + end; + end; + assert(integer(x)=Length(Result)); +end; + +function Base64One(c: Char): integer; + {$ifdef HASINLINE}inline;{$endif} +begin + result := ord(c); + if result>127 then + result := -1 else + result := BASE64DECODE[result]; +end; + +function Base64JSONStringToBytes(const JSONString: string; + var Bytes: TByteDynArray; withBase64Magic: boolean): boolean; +var i,bits,value,x,magiclen,len: cardinal; +begin + result := JSONString=''; + if result then + exit; + if withBase64Magic then + if comparemem(pointer(JSONString),@JSON_BASE64_MAGIC,sizeof(JSON_BASE64_MAGIC)) then + magiclen := JSON_BASE64_MAGIC_LEN else + {$ifndef UNICODE} + if JSONString[1]='?' then // handle UTF-8 decoding error on ANSI Delphi + magiclen := 1 else + {$endif} + exit else + magiclen := 0; // withBase64Magic=false + x := length(JSONString); + len := x-magiclen; + if len and 3<>0 then + exit; + if len=0 then + Bytes := nil else begin + if BASE64DECODE=nil then begin + SetLength(BASE64DECODE,128); + for i := 0 to 127 do + BASE64DECODE[i] := -1; + for i := 0 to high(BASE64) do + BASE64DECODE[ord(BASE64[i])] := i; + end; + len := (len shr 2)*3; + if Base64One(JSONString[x])<0 then begin + dec(len); + if Base64One(JSONString[x-1])<0 then + dec(len); + end; + SetLength(Bytes,len); + bits := 0; + value := 0; + len := 0; + for i := magiclen+1 to Length(JSONString) do begin + x := ord(JSONString[i]); // inlined Base64One(JSONString[i]) + if x>127 then + break; + x := cardinal(BASE64DECODE[x]); + if integer(x)<0 then + break; + value := value*64+x; + bits := bits+6; + if bits>=8 then begin + bits := bits-8; + x := value shr bits; + value := value and ((1 shl bits)-1); + Bytes[len] := x; + inc(len); + end; + end; + end; + result := len=cardinal(length(Bytes)); +end; + +function RTTIPropInfoTypeName(PropInfo: TRTTIPropInfo): string; +begin + result := ShortStringToString(@PropInfo^.PropType^.Name); +end; + +procedure GetPropsInfo(TypeInfo: TRTTITypeInfo; var PropNames: TStringDynArray; + var PropRTTI: TRTTIPropInfoDynArray); +var i,n: integer; + List: PPropList; +begin + n := GetPropList(PTypeInfo(TypeInfo),List); + SetLength(PropNames,n); + SetLength(PropRTTI,n); + for i := 0 to n-1 do begin + PropRTTI[i] := List^[i]; + PropNames[i] := ShortStringToString(@PropRTTI[i]^.Name); + end; + freemem(List); +end; + +function IsDateTime(PropInfo: TRTTIPropInfo): boolean; + {$ifdef HASINLINE}inline;{$endif} +begin + result := PropInfo^.PropType{$ifndef FPC}^{$endif}=TypeInfo(TDateTime); +end; + +type + // used to map a TPropInfo.GetProc/SetProc and retrieve its kind + PropWrap = packed record + FillBytes: array [0..SizeOf(Pointer)-2] of byte; + /// = $ff for a field address, or =$fe for a virtual method + Kind: byte; + end; + +function IsBlob(PropInfo: TRTTIPropInfo): boolean; + {$ifdef HASINLINE}inline;{$endif} +begin // we only handle plain TByteDynArray properties without getter/setter +{$ifdef FPC} + result := (PropInfo^.PropType=TypeInfo(TByteDynArray)) and + (PropInfo^.PropProcs and 3=ptField); +{$else} + result := (PropInfo^.PropType^=TypeInfo(TByteDynArray)) and + (PropWrap(PropInfo^.GetProc).Kind=$FF); +{$endif} +end; + +function GetTByteDynArrayProp(Instance: TObject; PropInfo: TRTTIPropInfo): PByteDynArray; + {$ifdef HASINLINE}inline;{$endif} +begin + result := Pointer(NativeUInt(Instance)+ + (NativeUInt(PropInfo^.GetProc){$ifndef FPC} and $00FFFFFF{$endif})); +end; + +function GetInstanceProp(Instance: TObject; PropInfo: TRTTIPropInfo): variant; +var obj: TObject; +begin + VarClear(result); + if (PropInfo=nil) or (Instance=nil) then + exit; + case PropInfo^.PropType^.Kind of + tkInt64{$ifdef FPC}, tkQWord{$endif}: + result := GetInt64Prop(Instance,PropInfo); + tkEnumeration, tkInteger, tkSet: + result := GetOrdProp(Instance,PropInfo); + {$ifdef NEXTGEN} + tkUString: + result := GetStrProp(Instance,PropInfo); + {$else} + {$ifdef FPC}tkAString,{$endif}tkLString: + result := GetStrProp(Instance,PropInfo); + tkWString: + result := GetWideStrProp(Instance,PropInfo); + {$ifdef UNICODE} + tkUString: + result := GetUnicodeStrProp(Instance,PropInfo); + {$endif UNICODE} + {$endif NEXTGEN} + tkFloat: + if IsDateTime(PropInfo) then + result := DateTimeToIso8601(GetFloatProp(Instance,PropInfo)) else + result := GetFloatProp(Instance,PropInfo); + tkVariant: + result := GetVariantProp(Instance,PropInfo); + tkClass: begin + obj := TObject(NativeInt(GetOrdProp(Instance,PropInfo))); + if obj=nil then + result := null else + TJSONVariantData(result).Init(ObjectToJSON(obj)); + end; + tkDynArray: + if IsBlob(PropInfo) then + result := BytesToBase64JSONString(GetTByteDynArrayProp(Instance,PropInfo)^); + end; +end; + +procedure SetInstanceProp(Instance: TObject; PropInfo: TRTTIPropInfo; + const Value: variant); +var blob: PByteDynArray; + obj: TObject; +begin + if (PropInfo<>nil) and (Instance<>nil) then + case PropInfo^.PropType^.Kind of + tkInt64{$ifdef FPC}, tkQWord{$endif}: + if TVarData(Value).VType=varInt64 then + SetInt64Prop(Instance,PropInfo,TVarData(Value).VInt64) else + SetOrdProp(Instance,PropInfo,Value); + tkEnumeration, tkInteger, tkSet: + SetOrdProp(Instance,PropInfo,Value); + {$ifdef NEXTGEN} + tkUString: + if TVarData(Value).VType<=varNull then + SetStrProp(Instance,PropInfo,'') else + SetStrProp(Instance,PropInfo,Value); + {$else} + {$ifdef FPC}tkAString,{$endif} tkLString: + if TVarData(Value).VType<=varNull then + SetStrProp(Instance,PropInfo,'') else + SetStrProp(Instance,PropInfo,Value); + tkWString: + if TVarData(Value).VType<=varNull then + SetWideStrProp(Instance,PropInfo,'') else + SetWideStrProp(Instance,PropInfo,Value); + {$ifdef UNICODE} + tkUString: + if TVarData(Value).VType<=varNull then + SetUnicodeStrProp(Instance,PropInfo,'') else + SetUnicodeStrProp(Instance,PropInfo,Value); + {$endif UNICODE} + {$endif NEXTGEN} + tkFloat: + if IsDateTime(PropInfo) and VarIsStr(Value) then + SetFloatProp(Instance,PropInfo,Iso8601ToDateTime(Value)) else + SetFloatProp(Instance,PropInfo,Value); + tkVariant: + SetVariantProp(Instance,PropInfo,Value); + tkDynArray: + if IsBlob(PropInfo) then begin + blob := GetTByteDynArrayProp(Instance,PropInfo); + if (TVarData(Value).VType<=varNull) or + not Base64JSONStringToBytes(Value,blob^) then + Finalize(blob^); + end; + tkClass: begin + obj := TObject(NativeInt(GetOrdProp(Instance,PropInfo))); + if TVarData(Value).VType>varNull then + if obj=nil then begin + obj := JSONVariantData(Value)^.ToNewObject; + if obj<>nil then + SetOrdProp(Instance,PropInfo,NativeInt(obj)); + end else + JSONVariantData(Value)^.ToObject(obj); + end; + end; +end; + +function JSONToObjectList(ItemClass: TClass; const JSON: string): TObjectList; +var doc: TJSONVariantData; + item: TObject; + i: integer; +begin + doc.Init(JSON); + if (doc.VKind<>jvArray) or (ItemClass=nil) then + result := nil else begin + result := TObjectList.Create; + for i := 0 to doc.Count-1 do begin + item := ItemClass.Create; + if not JSONVariantData(doc.Values[i])^.ToObject(item) then begin + FreeAndNil(result); + exit; + end; + result.Add(item); + end; + end; +end; + +function JSONToObject(Instance: TObject; const JSON: string): boolean; +var doc: TJSONVariantData; +begin + if Instance=nil then + result := false else begin + doc.Init(JSON); + result := doc.ToObject(Instance); + end; +end; + +function JSONToNewObject(const JSON: string): pointer; +var doc: TJSONVariantData; +begin + doc.Init(JSON); + result := doc.ToNewObject; +end; + +var + RegisteredClass: array of record + ClassName: string; + ClassType: TClass; + end; + +function FindClassForJSON(const ClassName: string): integer; +begin + for result := 0 to high(RegisteredClass) do + if IdemPropName(RegisteredClass[result].ClassName,ClassName) then + exit; + result := -1; +end; + +function CreateClassForJSON(const ClassName: string): TObject; +var i: integer; +begin + i := FindClassForJSON(ClassName); + if i<0 then + result := nil else + result := RegisteredClass[i].ClassType.Create; +end; + +procedure RegisterClassForJSON(const Classes: array of TClass); +var c,i: integer; + name: string; +begin + for c := 0 to high(Classes) do begin + name := string(Classes[c].ClassName); + i := FindClassForJSON(name); + if i>=0 then + continue; + i := length(RegisteredClass); + SetLength(RegisteredClass,i+1); + RegisteredClass[i].ClassName := Name; + RegisteredClass[i].ClassType := Classes[c]; + end; +end; + +function ObjectToJSON(Instance: TObject; StoreClassName: boolean): string; +var TypeInfo: PTypeInfo; + PropCount, i: integer; + PropList: PPropList; + PropInfo: PPropInfo; +begin + if Instance=nil then begin + result := 'null'; + exit; + end; + {$ifndef NEXTGEN} + if Instance.InheritsFrom(TList) then begin + if TList(Instance).Count=0 then + result := '[]' else begin + result := '['; + for i := 0 to TList(Instance).Count-1 do + result := result+ObjectToJSON(TObject( + TList(Instance).List{$ifdef FPC}^{$endif}[i]),StoreClassName)+','; + result[length(result)] := ']'; + end; + exit; + end; + {$endif} + if Instance.InheritsFrom(TStrings) then begin + if TStrings(Instance).Count=0 then + result := '[]' else begin + result := '['; + for i := 0 to TStrings(Instance).Count-1 do + result := result+StringToJSON(TStrings(Instance).Strings[i])+','; + result[length(result)] := ']'; + end; + exit; + end; + if Instance.InheritsFrom(TCollection) then begin + if TCollection(Instance).Count=0 then + result := '[]' else begin + result := '['; + for i := 0 to TCollection(Instance).Count-1 do + result := result+ObjectToJSON(TCollection(Instance).Items[i],StoreClassName)+','; + result[length(result)] := ']'; + end; + exit; + end; + TypeInfo := Instance.ClassInfo; + if TypeInfo=nil then begin + result := 'null'; + exit; + end; + PropCount := GetPropList(TypeInfo,PropList); + if PropCount>0 then + try + if StoreClassName then + result := '{"ClassName":"'+string(Instance.ClassName)+'",' else + result := '{'; + for i := 0 to PropCount-1 do begin + PropInfo := PropList^[i]; + result := result+StringToJSON(ShortStringToString(@PropInfo^.Name))+':'+ + ValueToJSON(GetInstanceProp(Instance,PropInfo))+','; + end; + result[length(result)] := '}'; + finally + FreeMem(PropList); + end else + result := 'null'; +end; + +procedure GetPublishedMethods(Instance: TObject; + out Methods: TPublishedMethodDynArray); +var n: integer; + procedure AddParentsFirst(C: TClass); + type + TMethodInfo = packed record + {$ifdef FPC} + Name: PShortString; + Addr: Pointer; + {$else} + Len: Word; + Addr: Pointer; + Name: Byte; + {$endif} + end; + var M: ^TMethodInfo; + Method: TMethod; + i,MCount: integer; + begin + if C=nil then + exit; + AddParentsFirst(C.ClassParent); // put children methods afterward + M := PPointer(NativeInt(C)+vmtMethodTable)^; + if M=nil then + exit; + Method.Data := Instance; + MCount := {$ifdef FPC}PCardinal{$else}PWord{$endif}(M)^; + inc({$ifdef FPC}PCardinal{$else}PWord{$endif}(M)); + for i := 1 to MCount do begin + Method.Code := M^.Addr; + if n>=length(Methods) then + SetLength(Methods,n+32); + Methods[n].Name := {$ifdef FPC}M^.Name^{$else}ShortStringToString(@M^.Name){$endif}; + Methods[n].Method := Method; + inc(n); + {$ifdef FPC} + inc(M); + {$else} + inc(PByte(M),M^.Len); + {$endif} + end; + end; +begin + if Instance=nil then + exit; + n := 0; + AddParentsFirst(Instance.ClassType); + SetLength(Methods,n); +end; + + +{ TJSONVariantData } + +procedure TJSONVariantData.Init; +begin + VType := JSONVariantType.VarType; + {$ifdef UNICODE} // makes compiler happy + _Align := 0; + {$endif} + VKind := jvUndefined; + VCount := 0; + SetLength(Names, 0); + SetLength(Values, 0); + pointer(Names) := nil; + pointer(Values) := nil; +end; + +procedure TJSONVariantData.Init(const JSON: string); +begin + Init; + FromJSON(JSON); + if VType=varNull then + VKind := jvObject else + if VType<>JSONVariantType.VarType then + Init; // we expect a true JSON array or object here +end; + +procedure TJSONVariantData.InitFrom(const aValues: TVariantDynArray); +begin + Init; + VKind := jvArray; + Values := aValues; +end; + +procedure TJSONVariantData.AddNameValue(const aName: string; + const aValue: variant); +begin + if VKind=jvUndefined then + VKind := jvObject else + if VKind<>jvObject then + raise EJSONException.CreateFmt('AddNameValue(%s) over array',[aName]); + if VCount<=length(Values) then begin + SetLength(Values,VCount+VCount shr 3+32); + SetLength(Names,VCount+VCount shr 3+32); + end; + Values[VCount] := aValue; + Names[VCount] := aName; + inc(VCount); +end; + +procedure TJSONVariantData.AddValue(const aValue: variant); +begin + if VKind=jvUndefined then + VKind := jvArray else + if VKind<>jvArray then + raise EJSONException.Create('AddValue() over object'); + if VCount<=length(Values) then + SetLength(Values,VCount+VCount shr 3+32); + Values[VCount] := aValue; + inc(VCount); +end; + +function TJSONVariantData.FromJSON(const JSON: string): boolean; +var Parser: TJSONParser; +begin + Parser.Init(JSON,1); + result := Parser.GetNextJSON(variant(self)) in [kObject,kArray]; +end; + +function TJSONVariantData.Data(const aName: string): PJSONVariantData; +var i: integer; +begin + i := NameIndex(aName); + if (i<0) or (TVarData(Values[i]).VType<>JSONVariantType.VarType) then + result := nil else + result := @Values[i]; +end; + +function TJSONVariantData.GetKind: TJSONVariantKind; +begin + if (@self=nil) or (VType<>JSONVariantType.VarType) then + result := jvUndefined else + result := VKind; +end; + +function TJSONVariantData.GetCount: integer; +begin + if (@self=nil) or (VType<>JSONVariantType.VarType) then + result := 0 else + result := VCount; +end; + +function TJSONVariantData.GetValue(const aName: string): variant; +begin + VarClear(result); + if (@self<>nil) and (VType=JSONVariantType.VarType) and (VKind=jvObject) then + GetVarData(aName,TVarData(result)); +end; + +function TJSONVariantData.GetValueCopy(const aName: string): variant; +var i: cardinal; +begin + VarClear(result); + if (@self<>nil) and (VType=JSONVariantType.VarType) and (VKind=jvObject) then begin + i := cardinal(NameIndex(aName)); + if inil) and (VType=JSONVariantType.VarType) and (VKind=jvArray) then + if cardinal(aIndex)nil) and (VType=JSONVariantType.VarType) and (VKind=jvArray) then + if cardinal(aIndex)nil) and (VType=JSONVariantType.VarType) and (Names<>nil) then + for result := 0 to VCount-1 do + if Names[result]=aName then + exit; + result := -1; +end; + +procedure TJSONVariantData.SetPath(const aPath: string; const aValue: variant); +var i: integer; +begin + for i := length(aPath) downto 1 do + if aPath[i]='.' then begin + EnsureData(copy(aPath,1,i-1))^.SetValue(copy(aPath,i+1,maxInt),aValue); + exit; + end; + SetValue(aPath,aValue); +end; + +function TJSONVariantData.EnsureData(const aPath: string): PJSONVariantData; +var i: integer; + new: TJSONVariantData; +begin // recursive value set + i := Pos('.',aPath); + if i=0 then begin + i := NameIndex(aPath); + if i<0 then begin // not existing: create new + new.Init; + AddNameValue(aPath,variant(new)); + result := @Values[VCount-1]; + end else begin + if TVarData(Values[i]).VType<>JSONVariantType.VarType then begin + VarClear(Values[i]); + TJSONVariantData(Values[i]).Init; // create as TJSONVariantData + end; + result := @Values[i]; + end; + end else + result := EnsureData(copy(aPath,1,i-1))^.EnsureData(copy(aPath,i+1,maxInt)); +end; + +function TJSONVariantData.AddItem: PJSONVariantData; +var new: TJSONVariantData; +begin + new.Init; + AddValue(variant(new)); + result := @Values[VCount-1]; +end; + +procedure TJSONVariantData.SetValue(const aName: string; + const aValue: variant); +var i: integer; +begin + if @self=nil then + raise EJSONException.Create('Unexpected Value[] access'); + if aName='' then + raise EJSONException.Create('Unexpected Value['''']'); + i := NameIndex(aName); + if i<0 then + AddNameValue(aName,aValue) else + Values[i] := aValue; +end; + +function TJSONVariantData.ToJSON: string; +var i: integer; +begin + case VKind of + jvObject: + if VCount=0 then + result := '{}' else begin + result := '{'; + for i := 0 to VCount-1 do + result := result+StringToJSON(Names[i])+':'+ValueToJSON(Values[i])+','; + result[length(result)] := '}'; + end; + jvArray: + if VCount=0 then + result := '[]' else begin + result := '['; + for i := 0 to VCount-1 do + result := result+ValueToJSON(Values[i])+','; + result[length(result)] := ']'; + end; + else result := 'null'; + end; +end; + +function TJSONVariantData.ToNewObject: TObject; +var ndx,i: Integer; +begin + result := nil; + if (Kind<>jvObject) or (Count=0) then + exit; + ndx := NameIndex('ClassName'); + if ndx<0 then + exit; + result := CreateClassForJSON(Values[ndx]); + if result=nil then + exit; // class name has not been registered + for i := 0 to Count-1 do + if i<>ndx then + SetInstanceProp(result,GetPropInfo(result,Names[i]),Values[i]); +end; + +function TJSONVariantData.ToObject(Instance: TObject): boolean; +var i: integer; + aItem: TCollectionItem; +begin + result := false; + if Instance=nil then + exit; + case Kind of + jvObject: + for i := 0 to Count-1 do + SetInstanceProp(Instance, + GetPropInfo(Instance,Names[i]),Values[i]); + jvArray: + if Instance.InheritsFrom(TCollection) then begin + TCollection(Instance).Clear; + for i := 0 to Count-1 do begin + aItem := TCollection(Instance).Add; + if not JSONVariantData(Values[i])^.ToObject(aItem) then + exit; + end; + end else + if Instance.InheritsFrom(TStrings) then + try + TStrings(Instance).BeginUpdate; + TStrings(Instance).Clear; + for i := 0 to Count-1 do + TStrings(Instance).Add(Values[i]); + finally + TStrings(Instance).EndUpdate; + end else + exit; + else + exit; + end; + result := true; +end; + + +{ TJSONVariant } + +procedure TJSONVariant.Cast(var Dest: TVarData; const Source: TVarData); +begin + CastTo(Dest,Source,VarType); +end; + +procedure TJSONVariant.CastTo(var Dest: TVarData; const Source: TVarData; + const AVarType: TVarType); +begin + if Source.VType<>VarType then + RaiseCastError; + variant(Dest) := TJSONVariantData(Source).ToJSON; +end; + +procedure TJSONVariant.Clear(var V: TVarData); +begin + V.VType := varEmpty; + Finalize(TJSONVariantData(V).Names); + Finalize(TJSONVariantData(V).Values); +end; + +procedure TJSONVariant.Copy(var Dest: TVarData; const Source: TVarData; + const Indirect: Boolean); +begin + if Indirect then + SimplisticCopy(Dest,Source,true) else begin + VarClear(variant(Dest)); + TJSONVariantData(Dest).Init; + TJSONVariantData(Dest) := TJSONVariantData(Source); + end; +end; + +{$ifndef FPC} +{$ifndef ISDELPHI6} +function TJSONVariant.FixupIdent(const AText: string): string; +begin // we expect the names to be case-sensitive + result := AText; +end; +{$endif} +{$endif} + +function TJSONVariant.GetProperty(var Dest: TVarData; const V: TVarData; + const Name: string): Boolean; +begin + if not TJSONVariantData(V).GetVarData(Name,Dest) then + Dest.VType := varNull; + result := true; +end; + +{$ifdef FPC_VARIANTSETVAR} +function TJSONVariant.SetProperty(var V: TVarData; const Name: string; + const Value: TVarData): Boolean; +{$else} +function TJSONVariant.SetProperty(const V: TVarData; const Name: string; + const Value: TVarData): Boolean; +{$endif} +begin + {$ifdef FPC} + {$ifndef FPC_VARIANTSETVAR} + raise EJSONException.Create('Setting TJSONVariant via late-binding does not'+ + ' work with FPC - see http://mantis.freepascal.org/view.php?id=26773 -'+ + ' use latest SVN or JSONVariantDataSafe(jsonvar)^[''prop''] := ... instead'); + {$endif} + {$endif} + TJSONVariantData(V).SetValue(Name,variant(Value)); + result := true; +end; + + +{ TJSONTable } + +constructor TJSONTable.Create(const aJSON: string); +var f,firstValue: integer; + EndOfField: char; + fieldCount, fieldName, dummy: variant; + Parser: TJSONParser; +begin + Parser.Init(aJSON,1); + fJSON := aJSON; + EndOfField := #0; + if (Parser.GetNextNonWhiteChar='{') and + Parser.CheckNextIdent('fieldCount') and + (Parser.GetNextJSON(fieldCount)=kInteger) and + (Parser.GetNextNonWhiteChar=',') and + Parser.CheckNextIdent('values') and + (Parser.GetNextNonWhiteChar='[') then begin + // non expanded format: {"fieldCount":2,"values":["ID","Int",1,0,2,0,3,...] + SetLength(fFieldNames,integer(fieldCount)); + for f := 0 to high(fFieldNames) do begin + if Parser.GetNextJSON(fieldName)<>kString then + exit; + fFieldNames[f] := fieldName; + EndOfField := Parser.GetNextNonWhiteChar; + if EndOfField<>',' then + if (EndOfField<>']') or (f<>High(FieldNames)) then + exit + end; + if EndOfField=',' then + fJSONIndexFirstValue := Parser.Index; + end else begin + // expanded format: [{"ID":1,"Int":0},{"ID":2,"Int":0},{"ID":3,...] + Parser.Index := 1; + if (Parser.GetNextNonWhiteChar='[') and + (Parser.GetNextNonWhiteChar='{') then begin + firstValue := Parser.Index; + f := 0; + repeat + if (Parser.GetNextJSON(fieldName)<>kString) or + (Parser.GetNextNonWhiteChar<>':') then + exit; + if Parser.GetNextJSON(dummy)=kNone then + exit; + SetLength(fFieldNames,f+1); + fFieldNames[f] := fieldName; + inc(f); + EndOfField := Parser.GetNextNonWhiteChar; + if EndOfField<>',' then + if EndOfField='}' then + break else + exit; + until false; + fJSONIndexFirstValue := firstValue; + fJSONExpanded := true; + end; + end; + SetLength(fRowValues,length(fFieldNames)); +end; + +function TJSONTable.FieldIndex(const FieldName: string): integer; +begin + for result := 0 to high(fFieldNames) do + if CompareText(fFieldNames[result],FieldName)=0 then + exit; + result := -1; +end; + +function TJSONTable.Get(const FieldName: string): variant; +var ndx: integer; +begin + ndx := FieldIndex(FieldName); + if ndx<0 then + result := null else + result := fRowValues[ndx]; +end; + +function TJSONTable.Step(SeekFirst: boolean): boolean; +var f: integer; + EndOfField: char; + Parser: TJSONParser; +begin + result := false; + if SeekFirst or (fJSONCurrentIndex=0) then + fJSONCurrentIndex := fJSONIndexFirstValue; + if fJSONCurrentIndex<=0 then + exit; + Parser.Init(fJSON,fJSONCurrentIndex); + fJSONCurrentIndex := -1; // indicates end of content in case of exit below + EndOfField := #0; + for f := 0 to high(fRowValues) do begin + if fJSONExpanded and not Parser.CheckNextIdent(fFieldNames[f]) then + exit; + if Parser.GetNextJSON(fRowValues[f])=kNone then + exit; + EndOfField := Parser.GetNextNonWhiteChar; + if EndOfField<>',' then + if f<>High(fRowValues) then + exit else + if ((EndOfField=']') and (not fJSONExpanded)) or + ((EndOfField='}') and fJSONExpanded) then + break else + exit; + end; + if fJSONExpanded then begin + if EndOfField<>'}' then + exit; + EndOfField := Parser.GetNextNonWhiteChar; + if (EndOfField=',') and + (Parser.GetNextNonWhiteChar<>'{') then + exit; + end; + if EndOfField=',' then + fJSONCurrentIndex := Parser.Index; // indicates next Step() has data + result := true; +end; + +function TJSONTable.StepValue(var RowValues: variant; SeekFirst: boolean): boolean; +begin + result := Step(SeekFirst); + if not result then + exit; + if TVarData(RowValues).VType<>JSONVariantType.VarType then begin + VarClear(RowValues); + TJSONVariantData(RowValues).Init; + end; + TJSONVariantData(RowValues).VKind := jvObject; + TJSONVariantData(RowValues).VCount := Length(fFieldNames); + TJSONVariantData(RowValues).Names := fFieldNames; + TJSONVariantData(RowValues).Values := fRowValues; +end; + + +{ TJSONTableObject } + +function TJSONTableObject.StepObject(Instance: TObject; SeekFirst: boolean=false): boolean; +begin + if (Instance=nil) then + result := false else + result := Step(SeekFirst); + if result then + FillInstance(Instance); +end; + +procedure TJSONTableObject.FillInstance(Instance: TObject); +var i: integer; +begin + if fTypeInfo<>Instance.ClassInfo then + FillPropInfo(Instance.ClassInfo); + for i := 0 to Length(fPropInfo)-1 do + SetInstanceProp(Instance,fPropInfo[i],fRowValues[i]); +end; + +function TJSONTableObject.GetPropInfo(aTypeInfo: TRTTITypeInfo; + const PropName: string): TRTTIPropInfo; +begin + result := TypInfo.GetPropInfo(PTypeInfo(aTypeInfo),PropName); +end; + +procedure TJSONTableObject.FillPropInfo(aTypeInfo: TRTTITypeInfo); +var i: integer; +begin + fTypeInfo := aTypeInfo; + SetLength(fPropInfo,Length(fFieldNames)); + for i := 0 to length(FieldNames)-1 do + fPropInfo[i] := GetPropInfo(aTypeInfo,fFieldNames[i]); +end; + + +initialization + JSONVariantType := TJSONVariant.Create; + {$ifndef FPC} + {$ifndef NOFORMATSETTINGS} + {$ifdef ISDELPHIXE} + SettingsUS := TFormatSettings.Create('en_US'); + {$else} + GetLocaleFormatSettings($0409,SettingsUS); + {$endif} + {$endif} + {$endif} +end. diff --git a/assets/InstructionEditor/screenshot.png b/assets/InstructionEditor/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6f78ea17672e083e603f5342036384842f3bed42 GIT binary patch literal 160907 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 SZ_GB) then + begin + Result := FormatFloat('0.00 GiB', Size / SZ_GB, Format); + end else if (Size >= SZ_MB) then + begin + Result := FormatFloat('0.00 MiB', Size / SZ_MB, Format); + end else if (Size >= SZ_KB) then + begin + Result := FormatFloat('0.00 KiB', Size / SZ_KB, Format); + end else + begin + Result := FormatFloat('0.00 Byte', Size, Format); + end; +end; + +{ TStringHelper } + +class function TStringHelper.IndexStr(const S: String; const Values: array of String): Integer; +var + L, I, J: Integer; + B: Boolean; +begin + Result := -1; + L := Length(S); + for I := Low(Values) to High(Values) do + begin + if (L <> Length(Values[I])) then Continue; + B := true; + for J := 1 to Length(S) do + begin + if (S[J] <> Values[I][J]) then + begin + B := false; + Continue; + end; + end; + if (B) then + begin + Result := I; + Break; + end; + end; +end; + +class procedure TStringHelper.AnsiLowerCase(var S: String); +const + Lower: array of Char = + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z']; +var + I: Integer; +begin + for I := 1 to Length(S) do + begin + if (CharInSet(S[I], ['A'..'Z'])) then + begin + S[I] := Lower[Ord(S[I]) - Ord('A')]; + end; + end; +end; + +{ TJSONHelper } + +class function TJSONHelper.JSONToString(JSON: PJSONVariantData; const Ident: String = ''): String; + +// TODO: This helper function needs refactoring + +procedure JSONToString(Buffer: TStringBuffer; JSON: PJSONVariantData; const Ident: String = ''); + +procedure ValueToJSON(Buffer: TStringBuffer; const Value: Variant); +var + I64: Int64; + B: Boolean; + S: String; +begin + if (TVarData(Value).VType = JSONVariantType.VarType) then + begin + JSONToString(Buffer, @TJSONVariantData(Value), Ident + ' '); + end else if (TVarData(Value).VType = varByRef or varVariant) then + begin + ValueToJSON(Buffer, PVariant(TVarData(Value).VPointer)^) + end else if (TVarData(Value).VType <= varNull) then + begin + Buffer.Append('null'); + end else if (TVarData(Value).VType = varBoolean) then + begin + B := Value; + Buffer.Append(LowerCase(BoolToStr(B, true))); + end else if (VarIsOrdinal(Value)) then + begin + I64 := Value; + Buffer.Append(IntToStr(I64)); + end else if (TVarData(Value).VType = varDate) then + begin + Buffer.Append(DateTimeToJSON(TVarData(Value).VDouble)); + end else if (VarIsFloat(Value)) then + begin + DoubleToJSON(Value, S); + Buffer.Append(S); + end else if (VarIsStr(Value)) then + begin + Buffer.Append(StringToJSON(Value)); + end else + begin + Buffer.Append(Value); + end; +end; + +var + I: Integer; +begin + case JSON.Kind of + jvObject: + begin + Buffer.AppendLn('{'); + for I := 0 to JSON.Count - 1 do + begin + Buffer.Append(Ident); + Buffer.Append(' '); + Buffer.Append(StringToJSON(JSON.Names[I]) + ': '); + ValueToJSON(Buffer, JSON.Values[I]); + if (I = JSON.Count - 1) then + begin + Buffer.AppendLn(''); + end else + begin + Buffer.AppendLn(','); + end; + end; + Buffer.Append(Ident); + Buffer.Append('}'); + end; + jvArray: + begin + Buffer.AppendLn('['); + for I := 0 to JSON.Count - 1 do + begin + Buffer.Append(Ident); + Buffer.Append(' '); + ValueToJSON(Buffer, JSON.Values[I]); + if (I = JSON.Count - 1) then + begin + Buffer.AppendLn(''); + end else + begin + Buffer.AppendLn(','); + end; + end; + Buffer.Append(Ident); + Buffer.Append(']'); + end else + begin + Buffer.Append('null'); + end; + end; +end; + +var + Buffer: TStringBuffer; +begin + Buffer := TStringBuffer.Create; + try + JSONToString(Buffer, JSON, Ident); + Result := Buffer.Value; + finally + Buffer.Free; + end; +end; + +{ TStringBuffer } + +procedure TStringBuffer.Append(const S: String); +var + L: Integer; +begin + L := Length(S); + while (FPosition + L > FCapacity) do + begin + Inc(FCapacity, FChunkSize); + SetLength(FBuffer, FCapacity); + end; + Move(S[1], FBuffer[FPosition], L * SizeOf(FBuffer[0])); + Inc(FPosition, L); +end; + +procedure TStringBuffer.AppendLn(const S: String); +begin + Append(S); + Append(sLineBreak); +end; + +constructor TStringBuffer.Create; +begin + inherited Create; + FChunkSize := 1024 * 16; + FCapacity := FChunkSize; + SetLength(FBuffer, FChunkSize); +end; + +function TStringBuffer.GetValue: String; +begin + SetLength(Result, FPosition); + Move(FBuffer[0], Result[1], FPosition * SizeOf(FBuffer[0])); +end; + +end. diff --git a/assets/InstructionEditor/untInstructionEditor.pas b/assets/InstructionEditor/untInstructionEditor.pas new file mode 100644 index 0000000..5c59904 --- /dev/null +++ b/assets/InstructionEditor/untInstructionEditor.pas @@ -0,0 +1,4611 @@ +unit untInstructionEditor; + +interface + +uses + System.Classes, System.Generics.Collections, SynCrossPlatformJSON; + +type + TInstructionEditor = class; + TInstructionDefinition = class; + TDefinitionContainer = class; + + TExtInstructionMode = ( + imNeutral, + imRequire64BitMode, + imExclude64BitMode + ); + + TExtMandatoryPrefix = ( + mpNone, + mpPrefix66, + mpPrefixF3, + mpPrefixF2 + ); + + TExtModrmMod = ( + mdNeutral, + mdMemory, + mdRegister + ); + + TExtModrmReg = ( + rgNeutral, + rg0, + rg1, + rg2, + rg3, + rg4, + rg5, + rg6, + rg7 + ); + + TExtModrmRm = ( + rmNeutral, + rm0, + rm1, + rm2, + rm3, + rm4, + rm5, + rm6, + rm7 + ); + + TExtOperandSize = ( + osNeutral, + os16Bit, + os32Bit + ); + + TExtAddressSize = ( + asNeutral, + as16Bit, + as32Bit, + as64Bit + ); + + TExtBitFilter = ( + bfRexW, + bfVexL, + bfEvexL2, + bfEvexB + ); + + TExtBitFilters = set of TExtBitFilter; + + TOpcodeExtensions = class(TPersistent) + strict private + FDefinition: TInstructionDefinition; + strict private + FMode: TExtInstructionMode; + FMandatoryPrefix: TExtMandatoryPrefix; + FModrmMod: TExtModrmMod; + FModrmReg: TExtModrmReg; + FModrmRm: TExtModrmRm; + FOperandSize: TExtOperandSize; + FAddressSize: TExtAddressSize; + FBitFilters: TExtBitFilters; + strict private + procedure SetMode(const Value: TExtInstructionMode); inline; + procedure SetMandatoryPrefix(const Value: TExtMandatoryPrefix); inline; + procedure SetModrmMod(const Value: TExtModrmMod); inline; + procedure SetModrmReg(const Value: TExtModrmReg); inline; + procedure SetModrmRm(const Value: TExtModrmRm); inline; + procedure SetOperandSize(const Value: TExtOperandSize); inline; + procedure SetAddressSize(const Value: TExtAddressSize); inline; + procedure SetBitFilters(const Value: TExtBitFilters); inline; + private + procedure Changed; inline; + private + procedure LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SaveToJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SetPrefix(const Value: TExtMandatoryPrefix); + protected + procedure AssignTo(Dest: TPersistent); override; + protected + constructor Create(Definition: TInstructionDefinition); + public + function Equals(const Value: TOpcodeExtensions): Boolean; reintroduce; + published + property Mode: TExtInstructionMode read FMode write SetMode default imNeutral; + property MandatoryPrefix: TExtMandatoryPrefix read FMandatoryPrefix write + SetMandatoryPrefix default mpNone; + property ModrmMod: TExtModrmMod read FModrmMod write SetModrmMod default mdNeutral; + property ModrmReg: TExtModrmReg read FModrmReg write SetModrmReg default rgNeutral; + property ModrmRm: TExtModrmRm read FModrmRm write SetModrmRm default rmNeutral; + property OperandSize: TExtOperandSize read FOperandSize write SetOperandSize default osNeutral; + property AddressSize: TExtAddressSize read FAddressSize write SetAddressSize default asNeutral; + property BitFilters: TExtBitFilters read FBitFilters write SetBitFilters default []; + end; + + TCPUIDFeatureFlag = ( + cf3DNOW, + cfADX, + cfAESNI, + cfAVX, + cfAVX2, + cfAVX512BW, + cfAVX512CD, + cfAVX512DQ, + cfAVX512ER, + cfAVX512F, + cfAVX512PF, + cfAVX512VL, + cfBMI1, + cfBMI2, + cfCMOV, + cfCMPXCHG16B, + cfF16C, + cfFMA, + cfFMA4, + cfFSGSBASE, + cfHLE, + cfLZCNT, + cfMMX, + cfMOVBE, + cfMPX, + cfPCLMUL, + cfPOPCNT, + cfPREFETCHW, + cfRDRAND, + cfRDSEED, + cfRTM, + cfSHA, + cfSSE1, + cfSSE2, + cfSSE3, + cfSSE41, + cfSSE42, + cfSSE4A, + cfSSSE3, + cfTBM, + cfTSX, + cfXOP, + cfFXSR, + cfLAHFSAHF, + cfXSAVE, + cfXSAVES, + cfXSAVEC, + cfXSAVEOPT, + cfMFENCE, + cfVBMI, + cfIFMA + ); + + TCPUIDFeatureFlagSet = set of TCPUIDFeatureFlag; + + TCPUIDFeatureFlags = class(TPersistent) + strict private + FDefinition: TInstructionDefinition; + FFeatureFlags: TCPUIDFeatureFlagSet; + strict private + procedure SetFeatureFlags(const Value: TCPUIDFeatureFlagSet); inline; + strict private + procedure Changed; inline; + private + procedure LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SaveToJSON(JSON: PJSONVariantData; const FieldName: String); + protected + procedure AssignTo(Dest: TPersistent); override; + protected + constructor Create(Definition: TInstructionDefinition); + public + function Equals(const Value: TCPUIDFeatureFlags): Boolean; reintroduce; + public + property FeatureFlags: TCPUIDFeatureFlagSet read FFeatureFlags write SetFeatureFlags; + end; + + TX86Register = ( + regNone, + // General purpose registers 64-bit + regRAX, regRCX, regRDX, regRBX, regRSP, regRBP, regRSI, regRDI, + regR8, regR9, regR10, regR11, regR12, regR13, regR14, regR15, + // General purpose registers 32-bit + regEAX, regECX, regEDX, regEBX, regESP, regEBP, regESI, regEDI, + regR8D, regr9D, regR10D, regR11D, regR12D, regR13D, regR14D, regR15D, + // General purpose registers 16-bit + regAX, regCX, regDX, regBX, regSP, regBP, regSI, regDI, + regR8W, regR9W, regR10W, regR11W, regR12W, regR13W, regR14W, regR15W, + // General purpose registers 8-bit + regAL, regCL, regDL, regBL, regAH, regCH, regDH, regBH, + regSPL, regBPL, regSIL, regDIL, + regR8B, regR9B, regR10B, regR11B, regR12B, regR13B, regR14B, regR15B, + // Floating point legacy registers + regST0, regST1, regST2, regST3, regST4, regST5, regST6, regST7, + // Floating point multimedia registers + regMM0, regMM1, regMM2, regMM3, regMM4, regMM5, regMM6, regMM7, + // Floating point vector registers 512-bit + regZMM0, regZMM1, regZMM2, regZMM3, regZMM4, regZMM5, regZMM6, regZMM7, + regZMM8, regZMM9, regZMM10, regZMM11, regZMM12, regZMM13, regZMM14, regZMM15, + regZMM16, regZMM17, regZMM18, regZMM19, regZMM20, regZMM21, regZMM22, regZMM23, + regZMM24, regZMM25, regZMM26, regZMM27, regZMM28, regZMM29, regZMM30, regZMM31, + // Floating point vector registers 256-bit + regYMM0, regYMM1, regYMM2, regYMM3, regYMM4, regYMM5, regYMM6, regYMM7, + regYMM8, regYMM9, regYMM10, regYMM11, regYMM12, regYMM13, regYMM14, regYMM15, + regYMM16, regYMM17, regYMM18, regYMM19, regYMM20, regYMM21, regYMM22, regYMM23, + regYMM24, regYMM25, regYMM26, regYMM27, regYMM28, regYMM29, regYMM30, regYMM31, + // Floating point vector registers 128-bit + regXMM0, regXMM1, regXMM2, regXMM3, regXMM4, regXMM5, regXMM6, regXMM7, + regXMM8, regXMM9, regXMM10, regXMM11, regXMM12, regXMM13, regXMM14, regXMM15, + regXMM16, regXMM17, regXMM18, regXMM19, regXMM20, regXMM21, regXMM22, regXMM23, + regXMM24, regXMM25, regXMM26, regXMM27, regXMM28, regXMM29, regXMM30, regXMM31, + // Special registers + regRFLAGS, regEFLAGS, regFLAGS, regRIP, regEIP, regIP, + // Segment registers + regES, regCS, regSS, regDS, regGS, regFS, + // Control registers + regCR0, regCR1, regCR2, regCR3, regCR4, regCR5, regCR6, regCR7, + regCR8, regCR9, regCR10, regCR11, regCR12, regCR13, regCR14, regCR15, + // Debug registers + regDR0, regDR1, regDR2, regDR3, regDR4, regDR5, regDR6, regDR7, + regDR8, regDR9, regDR10, regDR11, regDR12, regDR13, regDR14, regDR15, + // Mask registers + regK0, regK1, regK2, regK3, regK4, regK5, regK6, regK7, + // Bounds registers + regBND0, regBND1, regBND2, regBND3 + ); + + TX86RegisterSet = set of TX86Register; + + TX86Registers = class(TPersistent) + strict private + FDefinition: TInstructionDefinition; + FRegisters: TX86RegisterSet; + strict private + procedure SetRegisters(const Value: TX86RegisterSet); inline; + strict private + procedure Changed; inline; + private + procedure LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SaveToJSON(JSON: PJSONVariantData; const FieldName: String); + protected + procedure AssignTo(Dest: TPersistent); override; + protected + constructor Create(Definition: TInstructionDefinition); + public + function Equals(const Value: TX86Registers): Boolean; reintroduce; + public + property Registers: TX86RegisterSet read FRegisters write SetRegisters; + end; + + TX86FlagValue = ( + fvUnused, + fvTested, + fvModified, + fvReset, + fvSet, + fvUndefined, + fvPriorValue + ); + + TX86Flags = class(TPersistent) + strict private + FDefinition: TInstructionDefinition; + strict private + FCF: TX86FlagValue; + FPF: TX86FlagValue; + FAF: TX86FlagValue; + FZF: TX86FlagValue; + FSF: TX86FlagValue; + FTF: TX86FlagValue; + FIF: TX86FlagValue; + FDF: TX86FlagValue; + FOF: TX86FlagValue; + FRF: TX86FlagValue; + FVM: TX86FlagValue; + FAC: TX86FlagValue; + FVIF: TX86FlagValue; + FVIP: TX86FlagValue; + FID: TX86FlagValue; + strict private + procedure SetCF(const Value: TX86FlagValue); inline; + procedure SetPF(const Value: TX86FlagValue); inline; + procedure SetAF(const Value: TX86FlagValue); inline; + procedure SetZF(const Value: TX86FlagValue); inline; + procedure SetSF(const Value: TX86FlagValue); inline; + procedure SetTF(const Value: TX86FlagValue); inline; + procedure SetIF(const Value: TX86FlagValue); inline; + procedure SetDF(const Value: TX86FlagValue); inline; + procedure SetOF(const Value: TX86FlagValue); inline; + procedure SetRF(const Value: TX86FlagValue); inline; + procedure SetVM(const Value: TX86FlagValue); inline; + procedure SetAC(const Value: TX86FlagValue); inline; + procedure SetVIF(const Value: TX86FlagValue); inline; + procedure SetVIP(const Value: TX86FlagValue); inline; + procedure SetID(const Value: TX86FlagValue); inline; + strict private + procedure Changed; inline; + private + procedure LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SaveToJSON(JSON: PJSONVariantData; const FieldName: String); + protected + procedure AssignTo(Dest: TPersistent); override; + protected + constructor Create(Definition: TInstructionDefinition); + public + function Equals(const Value: TX86Flags): Boolean; reintroduce; + published + { FLAGS } + property FlagCF: TX86FlagValue read FCF write SetCF default fvUnused; + property FlagPF: TX86FlagValue read FPF write SetPF default fvUnused; + property FlagAF: TX86FlagValue read FAF write SetAF default fvUnused; + property FlagZF: TX86FlagValue read FZF write SetZF default fvUnused; + property FlagSF: TX86FlagValue read FSF write SetSF default fvUnused; + property FlagTF: TX86FlagValue read FTF write SetTF default fvUnused; + property FlagIF: TX86FlagValue read FIF write SetIF default fvUnused; + property FlagDF: TX86FlagValue read FDF write SetDF default fvUnused; + property FlagOF: TX86FlagValue read FOF write SetOF default fvUnused; + { EFLAGS } + property FlagRF: TX86FlagValue read FRF write SetRF default fvUnused; + property FlagVM: TX86FlagValue read FVM write SetVM default fvUnused; + property FlagAC: TX86FlagValue read FAC write SetAC default fvUnused; + property FlagVIF: TX86FlagValue read FVIF write SetVIF default fvUnused; + property FlagVIP: TX86FlagValue read FVIP write SetVIP default fvUnused; + property FlagID: TX86FlagValue read FID write SetID default fvUnused; + end; + + TInstructionOperands = class; + + TOperandType = ( + optUnused, + optGPR8, + optGPR16, + optGPR32, + optGPR64, + optFPR, + optVR64, + optVR128, + optVR256, + optVR512, + optCR, + optDR, + optSREG, + optMSKR, + optBNDR, + optMem, + optMem8, + optMem16, + optMem32, + optMem64, + optMem80, + optMem128, + optMem256, + optMem512, + optMem32Bcst2, + optMem32Bcst4, + optMem32Bcst8, + optMem32Bcst16, + optMem64Bcst2, + optMem64Bcst4, + optMem64Bcst8, + optMem64Bcst16, + optMem32VSIBX, + optMem32VSIBY, + optMem32VSIBZ, + optMem64VSIBX, + optMem64VSIBY, + optMem64VSIBZ, + optMem1616, + optMem1632, + optMem1664, + optMem112, + optMem224, + optImm8, + optImm8U, + optImm16, + optImm32, + optImm64, + optRel8, + optRel16, + optRel32, + optRel64, + optPtr1616, + optPtr1632, + optPtr1664, + optMoffs16, + optMoffs32, + optMoffs64, + optSrcIndex8, + optSrcIndex16, + optSrcIndex32, + optSrcIndex64, + optDstIndex8, + optDstIndex16, + optDstIndex32, + optDstIndex64, + optFixed1, + optFixedAL, + optFixedCL, + optFixedAX, + optFixedDX, + optFixedEAX, + optFixedRAX, + optFixedST0, + optFixedES, + optFixedSS, + optFixedCS, + optFixedDS, + optFixedFS, + optFixedGS + ); + + TOperandEncoding = ( + opeNone, + opeModrmReg, + opeModrmRm, + opeModrmRmCD1, + opeModrmRmCD2, + opeModrmRmCD4, + opeModrmRmCD8, + opeModrmRmCD16, + opeModrmRmCD32, + opeModrmRmCD64, + opeOpcodeBits, + opeVexVVVV, + opeEvexAAA, + opeImm8, + opeImm16, + opeImm32, + opeImm64 + ); + + TOperandAccessMode = ( + opaRead, + opaWrite, + opaReadWrite + ); + + TInstructionOperand = class(TPersistent) + strict private + FOperands: TInstructionOperands; + FType: TOperandType; + FEncoding: TOperandEncoding; + FAccessMode: TOperandAccessMode; + strict private + function GetConflictState: Boolean; + procedure SetType(const Value: TOperandType); inline; + procedure SetEncoding(const Value: TOperandEncoding); inline; + procedure SetAccessMode(const Value: TOperandAccessMode); inline; + strict private + procedure Changed; inline; + private + procedure LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SaveToJSON(JSON: PJSONVariantData; const FieldName: String); + protected + procedure AssignTo(Dest: TPersistent); override; + protected + constructor Create(Operands: TInstructionOperands); + public + function Equals(const Value: TInstructionOperand): Boolean; reintroduce; + public + function GetDescription(IncludeAccessMode: Boolean = true): String; + public + property HasConflicts: Boolean read GetConflictState; + published + property OperandType: TOperandType read FType write SetType default optUnused; + property Encoding: TOperandEncoding read FEncoding write SetEncoding default opeNone; + property AccessMode: TOperandAccessMode read FAccessMode write SetAccessMode default opaRead; + end; + + TInstructionOperands = class(TPersistent) + strict private + FDefinition: TInstructionDefinition; + FOperandA: TInstructionOperand; + FOperandB: TInstructionOperand; + FOperandC: TInstructionOperand; + FOperandD: TInstructionOperand; + strict private + function GetConflictState: Boolean; + private + procedure Changed; inline; + private + function GetOperandById(Id: Integer): TInstructionOperand; inline; + private + procedure LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); + procedure SaveToJSON(JSON: PJSONVariantData; const FieldName: String); + protected + procedure AssignTo(Dest: TPersistent); override; + protected + constructor Create(Definition: TInstructionDefinition); + public + function Equals(const Value: TInstructionOperands): Boolean; reintroduce; + public + destructor Destroy; override; + public + property HasConflicts: Boolean read GetConflictState; + published + property OperandA: TInstructionOperand read FOperandA; + property OperandB: TInstructionOperand read FOperandB; + property OperandC: TInstructionOperand read FOperandC; + property OperandD: TInstructionOperand read FOperandD; + end; + + TInstructionDefinitionConflict = ( + // This conflict is enforced by the user + idcForcedConflict, + // The instruction-operands configuration is invalid + idcOperands + ); + TInstructionDefinitionConflicts = set of TInstructionDefinitionConflict; + + TInstructionEncoding = ( + ieDefault, + ie3DNow, + ieXOP, + ieVEX, + ieEVEX + ); + + TOpcodeMap = ( + omDefault, + om0F, + om0F38, + om0F3A, + omXOP8, + omXOP9, + omXOPA + ); + + TOpcodeByte = type Byte; + + TInstructionDefinitionFlag = ( + ifForceConflict, + ifAcceptsLock, + ifAcceptsREP, + ifAcceptsXACQUIRE, + ifAcceptsXRELEASE, + ifAcceptsEVEXAAA, + ifAcceptsEVEXZ, + ifHasEVEXBC, + ifHasEVEXRC, + ifHasEVEXSAE + ); + TInstructionDefinitionFlags = set of TInstructionDefinitionFlag; + + TInstructionDefinition = class(TPersistent) + strict private + FEditor: TInstructionEditor; + FParent: TDefinitionContainer; + FConflicts: TInstructionDefinitionConflicts; + FData: Pointer; + FUpdateCount: Integer; + FDoUpdatePosition: Boolean; + FDoUpdateValues: Boolean; + strict private + FMnemonic: String; + FEncoding: TInstructionEncoding; + FOpcodeMap: TOpcodeMap; + FOpcode: TOpcodeByte; + FExtensions: TOpcodeExtensions; + FCPUID: TCPUIDFeatureFlags; + FOperands: TInstructionOperands; + FFlags: TInstructionDefinitionFlags; + FImplicitRead: TX86Registers; + FImplicitWrite: TX86Registers; + FX86Flags: TX86Flags; + FEVEXCD8Scale: Cardinal; + FComment: String; + strict private + function GetConflictState: Boolean; inline; + procedure SetMnemonic(const Value: String); inline; + procedure SetEncoding(const Value: TInstructionEncoding); inline; + procedure SetOpcodeMap(const Value: TOpcodeMap); inline; + procedure SetOpcode(const Value: TOpcodeByte); inline; + procedure SetFlags(const Value: TInstructionDefinitionFlags); inline; + procedure SetComment(const Value: String); inline; + strict private + procedure UpdateConflictFlags; + private + procedure UpdatePosition; inline; + procedure UpdateValues; inline; + procedure SetParent(Parent: TDefinitionContainer); inline; + protected + procedure AssignTo(Dest: TPersistent); override; + public + procedure BeginUpdate; inline; + procedure Update; inline; + procedure EndUpdate; inline; + public + function Equals(const Value: TInstructionDefinition): Boolean; reintroduce; + public + procedure LoadFromJSON(JSON: PJSONVariantData); + procedure SaveToJSON(JSON: PJSONVariantData); + public + constructor Create(Editor: TInstructionEditor; const Mnemonic: String); + destructor Destroy; override; + public + property Editor: TInstructionEditor read FEditor; + property Parent: TDefinitionContainer read FParent; + property HasConflicts: Boolean read GetConflictState; + property Data: Pointer read FData write FData; + published + property Mnemonic: String read FMnemonic write SetMnemonic; + property Encoding: TInstructionEncoding read FEncoding write SetEncoding default ieDefault; + property OpcodeMap: TOpcodeMap read FOpcodeMap write SetOpcodeMap default omDefault; + property Opcode: TOpcodeByte read FOpcode write SetOpcode; + property OpcodeExtensions: TOpcodeExtensions read FExtensions; + property CPUID: TCPUIDFeatureFlags read FCPUID; + property Operands: TInstructionOperands read FOperands; + property Flags: TInstructionDefinitionFlags read FFlags write SetFlags; + property ImplicitRead: TX86Registers read FImplicitRead; + property ImplicitWrite: TX86Registers read FImplicitWrite; + property X86Flags: TX86Flags read FX86Flags; + property EVEXCD8Scale: Cardinal read FEVEXCD8Scale default 0; + property Comment: String read FComment write SetComment; + property Conflicts: TInstructionDefinitionConflicts read FConflicts; + end; + + TInstructionFilterFlag = ( + // This is the root table + iffIsRootTable, + // This is a static filter that should not be removed. + // Warning: Never create static tables as child of non-static ones. The code assumes that the + // parent of a static-table is always another static table. + iffIsStaticFilter, + // This is a definition container and not an actual filter + iffIsDefinitionContainer + ); + TInstructionFilterFlags = set of TInstructionFilterFlag; + + TNeutralElementType = ( + // The neutral "zero" element is not supported + netNotAvailable, + // The neutral "zero" element is supported and used as a placeholder. The filter will signal a + // conflict, if the neutral element AND at least one regular value is set. + netPlaceholder, + // The neutral "zero" element is supported and can be used as a regular value + netValue + ); + + TInstructionFilterConflict = ( + // This filter is affected by a conflict of one or more child-filters + ifcInheritedConflict, + // This definition-container holds more than one instruction definition + ifcDefinitionCount, + // The neutral element and at least one regular value is set + ifcNeutralElement + ); + TInstructionFilterConflicts = set of TInstructionFilterConflict; + + TInstructionFilterClass = class of TInstructionFilter; + + PInstructionFilterList = ^TInstructionFilterList; + TInstructionFilterList = array of TInstructionFilterClass; + + TInstructionFilter = class(TPersistent) + strict private + FEditor: TInstructionEditor; + FParent: TInstructionFilter; + FItems: TArray; + FDefinitions: TList; + FConflicts: TInstructionFilterConflicts; + FInheritedConflicts: Integer; + FItemCount: Integer; + FData: Pointer; + strict private + FFilterFlags: TInstructionFilterFlags; + strict private + function GetItem(const Index: Integer): TInstructionFilter; inline; + function GetDefinition(const Index: Integer): TInstructionDefinition; inline; + function GetDefinitionCount: Integer; inline; + function GetConflictState: Boolean; inline; + procedure SetParent(Parent: TInstructionFilter); inline; + procedure SetConflicts(const Value: TInstructionFilterConflicts); inline; + strict private + procedure Changed; inline; + private + procedure SetItem(const Index: Integer; const Value: TInstructionFilter); inline; + private + procedure IncInheritedConflictCount; inline; + procedure DecInheritedConflictCount; inline; + private + procedure CreateFilterAtIndex(Index: Integer; FilterClass: TInstructionFilterClass; + IsRootTable, IsStaticFilter: Boolean); + procedure InsertDefinition(Definition: TInstructionDefinition); + procedure RemoveDefinition(Definition: TInstructionDefinition); + protected + constructor Create(Editor: TInstructionEditor; Parent: TInstructionFilter; + IsRootTable, IsStaticFilter: Boolean); virtual; + protected + property Definitions[const Index: Integer]: TInstructionDefinition read GetDefinition; + property DefinitionCount: Integer read GetDefinitionCount; + public + class function IsDefinitionContainer: Boolean; virtual; + class function GetNeutralElementType: TNeutralElementType; virtual; + class function GetCapacity: Cardinal; virtual; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; virtual; + class function GetDescription: String; virtual; + class function GetItemDescription(Index: Integer): String; virtual; + public + function IndexOf(const Filter: TInstructionFilter): Integer; + public + destructor Destroy; override; + public + property Editor: TInstructionEditor read FEditor; + property Parent: TInstructionFilter read FParent; + property Items[const Index: Integer]: TInstructionFilter read GetItem; + property HasConflicts: Boolean read GetConflictState; + property Data: Pointer read FData write FData; + published + property FilterFlags: TInstructionFilterFlags read FFilterFlags; + property NeutralElementType: TNeutralElementType read GetNeutralElementType; + property Capacity: Cardinal read GetCapacity; + property Conflicts: TInstructionFilterConflicts read FConflicts; + property ItemCount: Integer read FItemCount; + end; + + TDefinitionContainer = class(TInstructionFilter) + public + class function IsDefinitionContainer: Boolean; override; + public + property Definitions; + published + property DefinitionCount; + end; + + TEditorWorkStartEvent = + procedure(Sender: TObject; MinWorkCount, MaxWorkCount: Integer) of Object; + TEditorWorkEvent = + procedure(Sender: TObject; WorkCount: Integer) of Object; + TEditorFilterEvent = + procedure(Sender: TObject; Filter: TInstructionFilter) of Object; + TEditorDefinitionEvent = + procedure(Sender: TObject; Definition: TInstructionDefinition) of Object; + + TInstructionEditor = class(TObject) + strict private + class var FilterOrderDef: TInstructionFilterList; + class var FilterOrderXOP: TInstructionFilterList; + class var FilterOrderVEX: TInstructionFilterList; + class var FilterOrderEVEX: TInstructionFilterList; + strict private + class function GetFilterList(Encoding: TInstructionEncoding): PInstructionFilterList; inline; + strict private + FDefinitions: TList; + FRootTable: TInstructionFilter; + FFilterCount: Integer; + FUpdateCount: Integer; + FPreventDefinitionRemoval: Boolean; + strict private + FOnWorkStart: TEditorWorkStartEvent; + FOnWork: TEditorWorkEvent; + FOnWorkEnd: TNotifyEvent; + FOnBeginUpdate: TNotifyEvent; + FOnEndUpdate: TNotifyEvent; + FOnFilterCreated: TEditorFilterEvent; + FOnFilterInserted: TEditorFilterEvent; + FOnFilterChanged: TEditorFilterEvent; + FOnFilterRemoved: TEditorFilterEvent; + FOnFilterDestroyed: TEditorFilterEvent; + FOnDefinitionCreated: TEditorDefinitionEvent; + FOnDefinitionInserted: TEditorDefinitionEvent; + FOnDefinitionChanged: TEditorDefinitionEvent; + FOnDefinitionRemoved: TEditorDefinitionEvent; + FOnDefinitionDestroyed: TEditorDefinitionEvent; + strict private + function GetDefinition(const Index: Integer): TInstructionDefinition; inline; + function GetDefinitionCount: Integer; inline; + strict private + function GetDefinitionTopLevelFilter(Definition: TInstructionDefinition): TInstructionFilter; + private + procedure RegisterDefinition(Definition: TInstructionDefinition); inline; + procedure InsertDefinition(Definition: TInstructionDefinition); + procedure RemoveDefinition(Definition: TInstructionDefinition); inline; + procedure UnregisterDefinition(Definition: TInstructionDefinition); inline; + private + procedure FilterCreated(Filter: TInstructionFilter); inline; + procedure FilterInserted(Filter: TInstructionFilter); inline; + procedure FilterChanged(Filter: TInstructionFilter); inline; + procedure FilterRemoved(Filter: TInstructionFilter); inline; + procedure FilterDestroyed(Filter: TInstructionFilter); inline; + procedure DefinitionInserted(Definition: TInstructionDefinition); inline; + procedure DefinitionChanged(Definition: TInstructionDefinition); inline; + procedure DefinitionRemoved(Definition: TInstructionDefinition); inline; + public + class constructor Create; + public + procedure BeginUpdate; inline; + procedure EndUpdate; inline; + public + procedure LoadFromJSON(JSON: PJSONVariantData); + procedure SaveToJSON(JSON: PJSONVariantData); + procedure LoadFromFile(const Filename: String); + procedure SaveToFile(const Filename: String); + procedure Reset; + public + function CreateDefinition(const Mnemonic: String): TInstructionDefinition; inline; + public + constructor Create; + destructor Destroy; override; + public + property RootTable: TInstructionFilter read FRootTable; + property FilterCount: Integer read FFilterCount; + property Definitions[const Index: Integer]: TInstructionDefinition read GetDefinition; + property DefinitionCount: Integer read GetDefinitionCount; + public + property OnWorkStart: TEditorWorkStartEvent read FOnWorkStart write FOnWorkStart; + property OnWork: TEditorWorkEvent read FOnWork write FOnWork; + property OnWorkEnd: TNotifyEvent read FOnWorkEnd write FOnWorkEnd; + property OnBeginUpdate: TNotifyEvent read FOnBeginUpdate write FOnBeginUpdate; + property OnEndUpdate: TNotifyEvent read FOnEndUpdate write FOnEndUpdate; + property OnFilterCreated: TEditorFilterEvent read FOnFilterCreated write FOnFilterCreated; + property OnFilterInserted: TEditorFilterEvent read FOnFilterInserted write FOnFilterInserted; + property OnFilterChanged: TEditorFilterEvent read FOnFilterChanged write FOnFilterChanged; + property OnFilterRemoved: TEditorFilterEvent read FOnFilterRemoved write FOnFilterRemoved; + property OnFilterDestroyed: TEditorFilterEvent read FOnFilterDestroyed write FOnFilterDestroyed; + property OnDefinitionCreated: TEditorDefinitionEvent read FOnDefinitionCreated write + FOnDefinitionCreated; + property OnDefinitionInserted: TEditorDefinitionEvent read FOnDefinitionInserted write + FOnDefinitionInserted; + property OnDefinitionChanged: TEditorDefinitionEvent read FOnDefinitionChanged write + FOnDefinitionChanged; + property OnDefinitionRemoved: TEditorDefinitionEvent read FOnDefinitionRemoved write + FOnDefinitionRemoved; + property OnDefinitionDestroyed: TEditorDefinitionEvent read FOnDefinitionDestroyed write + FOnDefinitionDestroyed; + end; + + TTableGeneratorStatistics = record + public + FilterCount: Integer; + FilterSize: Cardinal; + DefinitionCount: Integer; + DefinitionSize: Cardinal; + MnemonicCount: Integer; + MnemonicSize: Cardinal; + end; + + TGeneratorWorkOperation = ( + woIndexingDefinitions, + woIndexingFilters, + woGeneratingFilterFiles, + woGeneratingDefinitionFiles, + woGeneratingMnemonicFiles + ); + + TGeneratorWorkStartEvent = + procedure(Sender: TObject; Operation: TGeneratorWorkOperation; MinWorkCount, + MaxWorkCount: Integer) of Object; + TGeneratorWorkEvent = + procedure(Sender: TObject; WorkCount: Integer) of Object; + + TTableGenerator = class(TObject) + strict private type + TIndexedFilterItem = record + public + Id: Integer; + Filter: TInstructionFilter; + Items: array of TIndexedFilterItem; + IsRedirect: Boolean; + end; + TIndexedDefinitionItem = record + public + Id: Integer; + Definition: TInstructionDefinition; + end; + strict private type + PIndexedFilterList = ^TIndexedFilterList; + TIndexedFilterList = TArray>>; + PIndexedDefinitionList = ^TIndexedDefinitionList; + TIndexedDefinitionList = TArray; + PMnemonicList = ^TMnemonicList; + TMnemonicList = TArray; + strict private + FStatistics: TTableGeneratorStatistics; + strict private + FOnWorkStart: TGeneratorWorkStartEvent; + FOnWork: TGeneratorWorkEvent; + FOnWorkEnd: TNotifyEvent; + strict private + procedure WorkStart(Operation: TGeneratorWorkOperation; MinWorkCount: Integer; + MaxWorkCount: Integer); inline; + procedure Work(WorkCount: Integer); inline; + procedure WorkEnd; inline; + strict private + procedure CreateEntityLists(Editor: TInstructionEditor; + var FilterList: TIndexedFilterList; + var DefinitionList: TIndexedDefinitionList; + var MnemonicList: TMnemonicList); + strict private + procedure GenerateInstructionTable(const OutputDirectory: String; + const FilterList: PIndexedFilterList; FilterCount: Integer); + procedure GenerateDefinitionList(const OutputDirectory: String; + const DefinitionList: PIndexedDefinitionList); + procedure GenerateMnemonicLists(const OutputDirectory: String; + const MnemonicList: PMnemonicList); + public + procedure GenerateFiles(Editor: TInstructionEditor; const OutputDirectory: String); + public + constructor Create; + destructor Destroy; override; + public + property Statistics: TTableGeneratorStatistics read FStatistics; + public + property OnWorkStart: TGeneratorWorkStartEvent read FOnWorkStart write FOnWorkStart; + property OnWork: TGeneratorWorkEvent read FOnWork write FOnWork; + property OnWorkEnd: TNotifyEvent read FOnWorkEnd write FOnWorkEnd; + end; + +implementation + +uses + System.SysUtils, System.Variants, System.TypInfo, System.Generics.Defaults, untHelperClasses, + untInstructionFilters; + +{$REGION 'Const: JSON strings for TOpcodeExtensions'} +const + SExtInstructionMode: array[TExtInstructionMode] of String = ( + 'neutral', + 'require64', + 'exclude64' + ); + + SExtMandatoryPrefix: array[TExtMandatoryPrefix] of String = ( + 'none', + '66', + 'f3', + 'f2' + ); + + SExtModrmMod: array[TExtModrmMod] of String = ( + 'neutral', + 'memory', + 'register' + ); + + SExtModrmReg: array[TExtModrmReg] of String = ( + 'neutral', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7' + ); + + SExtModrmRm: array[TExtModrmRm] of String = ( + 'neutral', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7' + ); + + SExtOperandSize: array[TExtOperandSize] of String = ( + 'default', + '16', + '32' + ); + + SExtAddressSize: array[TExtAddressSize] of String = ( + 'default', + '16', + '32', + '64' + ); + + SExtBitFilter: array[TExtBitFilter] of String = ( + 'rex_w', + 'vex_l', + 'evex_l2', + 'evex_b' + ); +{$ENDREGION} + +{$REGION 'Const: JSON strings for TCPUIDFeatureFlags'} +const + SCPUIDFeatureFlag: array[TCPUIDFeatureFlag] of String = ( + '3dnow', + 'adx', + 'aesni', + 'avx', + 'avx2', + 'avx512bw', + 'avx512cd', + 'avx512dq', + 'avx512er', + 'avx512f', + 'avx512pf', + 'avx512vl', + 'bmi1', + 'bmi2', + 'cmov', + 'cmpxchg16b', + 'f16c', + 'fma', + 'fma4', + 'fsgsbase', + 'hle', + 'lzcnt', + 'mmx', + 'movbe', + 'mpx', + 'pclmul', + 'popcnt', + 'prefetchw', + 'rdrand', + 'rdseed', + 'rtm', + 'sha', + 'sse1', + 'sse2', + 'sse3', + 'sse41', + 'sse42', + 'sse4a', + 'ssse3', + 'tbm', + 'tsx', + 'xop', + 'fxsr', + 'lahfsahf', + 'xsave', + 'xsaves', + 'xsavec', + 'xsaveopt', + 'mfence', + 'vbmi', + 'ifma' + ); +{$ENDREGION} + +{$REGION 'Const: JSON strings for TX86Registers'} +const + SX86Register: array[TX86Register] of String = ( + 'none', + // General purpose registers 64-bit + 'rax', 'rcx', 'rdx', 'rbx', 'rsp', 'rbp', 'rsi', 'rdi', + 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', + // General purpose registers 32-bit + 'eax', 'ecx', 'edx', 'ebx', 'esp', 'ebp', 'esi', 'edi', + 'r8d', 'r9d', 'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d', + // General purpose registers 16-bit + 'ax', 'cx', 'dx', 'bx', 'sp', 'bp', 'si', 'di', + 'r8w', 'r9w', 'r10w', 'r11w', 'r12w', 'r13w', 'r14w', 'r15w', + // General purpose registers 8-bit + 'al', 'cl', 'dl', 'bl', 'ah', 'ch', 'dh', 'bh', + 'spl', 'bpl', 'sil', 'dil', + 'r8b', 'r9b', 'r10b', 'r11b', 'r12b', 'r13b', 'r14b', 'r15b', + // Floating point legacy registers + 'st0', 'st1', 'st2', 'st3', 'st4', 'st5', 'st6', 'st7', + // Floating point multimedia registers + 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6', 'mm7', + // Floating point vector registers 512-bit + 'zmm0', 'zmm1', 'zmm2', 'zmm3', 'zmm4', 'zmm5', 'zmm6', 'zmm7', + 'zmm8', 'zmm9', 'zmm10', 'zmm11', 'zmm12', 'zmm13', 'zmm14', 'zmm15', + 'zmm16', 'zmm17', 'zmm18', 'zmm19', 'zmm20', 'zmm21', 'zmm22', 'zmm23', + 'zmm24', 'zmm25', 'zmm26', 'zmm27', 'zmm28', 'zmm29', 'zmm30', 'zmm31', + // Floating point vector registers 256-bit + 'ymm0', 'ymm1', 'ymm2', 'ymm3', 'ymm4', 'ymm5', 'ymm6', 'ymm7', + 'ymm8', 'ymm9', 'ymm10', 'ymm11', 'ymm12', 'ymm13', 'ymm14', 'ymm15', + 'ymm16', 'ymm17', 'ymm18', 'ymm19', 'ymm20', 'ymm21', 'ymm22', 'ymm23', + 'ymm24', 'ymm25', 'ymm26', 'ymm27', 'ymm28', 'ymm29', 'ymm30', 'ymm31', + // Floating point vector registers 128-bit + 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7', + 'xmm8', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14', 'xmm15', + 'xmm16', 'xmm17', 'xmm18', 'xmm19', 'xmm20', 'xmm21', 'xmm22', 'xmm23', + 'xmm24', 'xmm25', 'xmm26', 'xmm27', 'xmm28', 'xmm29', 'xmm30', 'xmm31', + // Special registers + 'rflags', 'eflags', 'flags', 'rip', 'eip', 'ip', + // Segment registers + 'es', 'cs', 'ss', 'ds', 'gs', 'fs', + // Control registers + 'cr0', 'cr1', 'cr2', 'cr3', 'cr4', 'cr5', 'cr6', 'cr7', + 'cr8', 'cr9', 'cr10', 'cr11', 'cr12', 'cr13', 'cr14', 'cr15', + // Debug registers + 'dr0', 'dr1', 'dr2', 'dr3', 'dr4', 'dr5', 'dr6', 'dr7', + 'dr8', 'dr9', 'dr10', 'dr11', 'dr12', 'dr13', 'dr14', 'dr15', + // Mask registers + 'k0', 'k1', 'k2', 'k3', 'k4', 'k5', 'k6', 'k7', + // Bounds registers + 'bnd0', 'bnd1', 'bnd2', 'bnd3' + ); +{$ENDREGION} + +{$REGION 'Const: JSON strings for TX86Flags'} +const + SX86FlagValue: array[TX86FlagValue] of String = ( + 'unused', + 'tested', + 'modified', + 'reset', + 'set', + 'undefined', + 'prior' + ); +{$ENDREGION} + +{$REGION 'Const: JSON strings for TInstructionOperand'} +const + SOperandType: array[TOperandType] of String = ( + 'unused', + 'gpr8', + 'gpr16', + 'gpr32', + 'gpr64', + 'fpr', + 'vr64', + 'vr128', + 'vr256', + 'vr512', + 'cr', + 'dr', + 'sreg', + 'mskr', + 'bndr', + 'mem', + 'mem8', + 'mem16', + 'mem32', + 'mem64', + 'mem80', + 'mem128', + 'mem256', + 'mem512', + 'mem32bcst2', + 'mem32bcst4', + 'mem32bcst8', + 'mem32bcst16', + 'mem64bcst2', + 'mem64bcst4', + 'mem64bcst8', + 'mem64bcst16', + 'mem32vsibx', + 'mem32vsiby', + 'mem32vsibz', + 'mem64vsibx', + 'mem64vsiby', + 'mem64vsibz', + 'mem1616', + 'mem1632', + 'mem1664', + 'mem112', + 'mem224', + 'imm8', + 'imm8u', + 'imm16', + 'imm32', + 'imm64', + 'rel8', + 'rel16', + 'rel32', + 'rel64', + 'ptr1616', + 'ptr1632', + 'ptr1664', + 'moffs16', + 'moffs32', + 'moffs64', + 'srcidx8', + 'srcidx16', + 'srcidx32', + 'srcidx64', + 'dstidx8', + 'dstidx16', + 'dstidx32', + 'dstidx64', + '1', + 'al', + 'cl', + 'ax', + 'dx', + 'eax', + 'rax', + 'st0', + 'es', + 'ss', + 'cs', + 'ds', + 'fs', + 'gs' + ); + + SOperandEncoding: array[TOperandEncoding] of String = ( + 'none', + 'modrm_reg', + 'modrm_rm', + 'modrm_rm_cd1', + 'modrm_rm_cd2', + 'modrm_rm_cd4', + 'modrm_rm_cd8', + 'modrm_rm_cd16', + 'modrm_rm_cd32', + 'modrm_rm_cd64', + 'opcode', + 'vex_vvvv', + 'evex_aaa', + 'imm8', + 'imm16', + 'imm32', + 'imm64' + ); + + SOperandAccessMode: array[TOperandAccessMode] of String = ( + 'read', + 'write', + 'readwrite' + ); +{$ENDREGION} + +{$REGION 'Const: JSON strings for TInstructionDefinition'} +const + SInstructionEncoding: array[TInstructionEncoding] of String = ( + 'default', + '3dnow', + 'xop', + 'vex', + 'evex' + ); + + SOpcodeMap: array[TOpcodeMap] of String = ( + 'default', + '0f', + '0f38', + '0f3a', + 'xop8', + 'xop9', + 'xopa' + ); + + SInstructionDefinitionFlag: array[TInstructionDefinitionFlag] of String = ( + 'conflict', + 'accepts_lock', + 'accepts_rep', + 'accepts_xacquire', + 'accepts_xrelease', + 'accepts_evex_aaa', + 'accepts_evex_z', + 'has_evex_bc', + 'has_evex_rc', + 'has_evex_sae' + ); +{$ENDREGION} + +{$REGION 'Class: TJSONEnumHelper'} +type + TJSONEnumHelper = record + private + class function ReadString(JSON: PJSONVariantData; const Name, Default: String; + const LowerCase: Boolean = true): String; static; inline; + public + class function ReadEnumValueFromString(JSON: PJSONVariantData; const Name: String; + const Values: array of String): Integer; static; + end; + +class function TJSONEnumHelper.ReadEnumValueFromString(JSON: PJSONVariantData; const Name: String; + const Values: array of String): Integer; +begin + Result := TStringHelper.IndexStr(ReadString(JSON, Name, Values[0]), Values); + if (Result < 0) then + begin + raise Exception.CreateFmt('The "%s" field contains an invalid enum value.', [Name]); + end; +end; + +class function TJSONEnumHelper.ReadString(JSON: PJSONVariantData; const Name, Default: String; + const LowerCase: Boolean): String; +var + V: Variant; +begin + V := JSON^.Value[Name]; + if (VarIsEmpty(V)) then + begin + Exit(Default); + end; + Result := V; + if (LowerCase) then + begin + TStringHelper.AnsiLowerCase(Result); + end; +end; +{$ENDREGION} + +{$REGION 'Class: TOpcodeExtensions'} +procedure TOpcodeExtensions.AssignTo(Dest: TPersistent); +var + D: TOpcodeExtensions; +begin + if (Dest is TOpcodeExtensions) then + begin + D := Dest as TOpcodeExtensions; + D.FMode := FMode; + D.FMandatoryPrefix := FMandatoryPrefix; + D.FModrmMod := FModrmMod; + D.FModrmReg := FModrmReg; + D.FModrmRm := FModrmRm; + D.FOperandSize := FOperandSize; + D.FAddressSize := FAddressSize; + D.FBitFilters := FBitFilters; + D.Changed; + end else inherited; +end; + +procedure TOpcodeExtensions.Changed; +begin + FDefinition.UpdatePosition; +end; + +constructor TOpcodeExtensions.Create(Definition: TInstructionDefinition); +begin + inherited Create; + FDefinition := Definition; +end; + +function TOpcodeExtensions.Equals(const Value: TOpcodeExtensions): Boolean; +begin + Result := + (Value.FMode = FMode) and (Value.FMandatoryPrefix = FMandatoryPrefix) and + (Value.FModrmMod = FModrmMod) and (Value.FModrmReg = FModrmReg) and + (Value.FModrmRm = FModrmRm) and (Value.FOperandSize = FOperandSize) and + (Value.FAddressSize = FAddressSize) and (Value.FBitFilters = FBitFilters); +end; + +procedure TOpcodeExtensions.LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); +var + V, A: PJSONVariantData; + I: Integer; + F: TExtBitFilter; + BitFilters: TExtBitFilters; +begin + V := JSON.Data(FieldName); + if (Assigned(V)) then + begin + if (V^.Kind <> jvObject) then + begin + raise Exception.CreateFmt('The "%s" field is not a valid JSON object.', [FieldName]); + end; + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'mode', SExtInstructionMode); + SetMode(TExtInstructionMode(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'prefix', SExtMandatoryPrefix); + SetPrefix(TExtMandatoryPrefix(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'modrm_mod', SExtModrmMod); + SetModrmMod(TExtModrmMod(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'modrm_reg', SExtModrmReg); + SetModrmReg(TExtModrmReg(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'modrm_rm', SExtModrmRm); + SetModrmRm(TExtModrmRm(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'opsize', SExtOperandSize); + SetOperandSize(TExtOperandSize(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'adsize', SExtAddressSize); + SetAddressSize(TExtAddressSize(I)); + A := V^.Data('bitfilters'); + if (Assigned(A)) then + begin + if (A^.Kind <> jvArray) then + begin + raise Exception.Create('The "prefix_flags" field is not a valid JSON array.'); + end; + BitFilters := []; + for I := 0 to A^.Count - 1 do + begin + for F := Low(SExtBitFilter) to High(SExtBitFilter) do + begin + if (LowerCase(A^.Item[I]) = SExtBitFilter[F]) then + begin + BitFilters := BitFilters + [F]; + Break; + end; + end; + end; + SetBitFilters(BitFilters); + end; + end; +end; + +procedure TOpcodeExtensions.SaveToJSON(JSON: PJSONVariantData; const FieldName: String); +var + V, A: TJSONVariantData; + F: TExtBitFilter; +begin + V.Init; + if (FMode <> imNeutral) then + V.AddNameValue('mode', SExtInstructionMode[FMode]); + if (FMandatoryPrefix <> mpNone) then + V.AddNameValue('prefix', SExtMandatoryPrefix[FMandatoryPrefix]); + if (FModrmMod <> mdNeutral) then + V.AddNameValue('modrm_mod', SExtModrmMod[FModrmMod]); + if (FModrmRm <> rmNeutral) then + V.AddNameValue('modrm_rm', SExtModrmRm[FModrmRm]); + if (FModrmReg <> rgNeutral) then + V.AddNameValue('modrm_reg', SExtModrmReg[FModrmReg]); + if (FOperandSize <> osNeutral) then + V.AddNameValue('opsize', SExtOperandSize[FOperandSize]); + if (FAddressSize <> asNeutral) then + V.AddNameValue('adsize', SExtAddressSize[FAddressSize]); + A.Init; + for F in FBitFilters do + begin + A.AddValue(SExtBitFilter[F]); + end; + if (A.Count > 0) then + begin + V.AddNameValue('bitfilters', Variant(A)); + end; + if (V.Count > 0) then + begin + JSON^.AddNameValue(FieldName, Variant(V)); + end; +end; + +procedure TOpcodeExtensions.SetAddressSize(const Value: TExtAddressSize); +begin + if (FAddressSize <> Value) then + begin + FAddressSize := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetBitFilters(const Value: TExtBitFilters); +begin + if (FBitFilters <> Value) then + begin + FBitFilters := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetMandatoryPrefix(const Value: TExtMandatoryPrefix); +begin + if (FMandatoryPrefix <> Value) then + begin + FMandatoryPrefix := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetMode(const Value: TExtInstructionMode); +begin + if (FMode <> Value) then + begin + FMode := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetModrmMod(const Value: TExtModrmMod); +begin + if (FModrmMod <> Value) then + begin + FModrmMod := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetModrmReg(const Value: TExtModrmReg); +begin + if (FModrmReg <> Value) then + begin + FModrmReg := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetModrmRm(const Value: TExtModrmRm); +begin + if (FModrmRm <> Value) then + begin + FModrmRm := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetOperandSize(const Value: TExtOperandSize); +begin + if (FOperandSize <> Value) then + begin + FOperandSize := Value; + Changed; + end; +end; + +procedure TOpcodeExtensions.SetPrefix(const Value: TExtMandatoryPrefix); +begin + FMandatoryPrefix := Value; +end; +{$ENDREGION} + +{$REGION 'Class: TCPUIDFeatureFlags'} +procedure TCPUIDFeatureFlags.AssignTo(Dest: TPersistent); +var + D: TCPUIDFeatureFlags; +begin + if (Dest is TCPUIDFeatureFlags) then + begin + D := Dest as TCPUIDFeatureFlags; + D.SetFeatureFlags(FFeatureFlags); + end else inherited; +end; + +procedure TCPUIDFeatureFlags.Changed; +begin + FDefinition.UpdateValues; +end; + +constructor TCPUIDFeatureFlags.Create(Definition: TInstructionDefinition); +begin + inherited Create; + FDefinition := Definition; +end; + +function TCPUIDFeatureFlags.Equals(const Value: TCPUIDFeatureFlags): Boolean; +begin + Result := (Value.FFeatureFlags = FFeatureFlags); +end; + +procedure TCPUIDFeatureFlags.LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); +var + A: PJSONVariantData; + I: Integer; + C: TCPUIDFeatureFlag; + Value: TCPUIDFeatureFlagSet; +begin + A := JSON.Data(FieldName); + if (Assigned(A)) then + begin + if (A^.Kind <> jvArray) then + begin + raise Exception.CreateFmt('The "%s" field is not a valid JSON array.', [FieldName]); + end; + Value := []; + for I := 0 to A^.Count - 1 do + begin + for C := Low(SCPUIDFeatureFlag) to High(SCPUIDFeatureFlag) do + begin + if (LowerCase(A^.Item[I]) = SCPUIDFeatureFlag[C]) then + begin + Value := Value + [C]; + Break; + end; + end; + end; + SetFeatureFlags(Value); + end; +end; + +procedure TCPUIDFeatureFlags.SaveToJSON(JSON: PJSONVariantData; const FieldName: String); +var + A: TJSONVariantData; + C: TCPUIDFeatureFlag; +begin + A.Init; + for C in FFeatureFlags do + begin + A.AddValue(SCPUIDFeatureFlag[C]); + end; + if (A.Count > 0) then + begin + JSON.AddNameValue(FieldName, Variant(A)); + end; +end; + +procedure TCPUIDFeatureFlags.SetFeatureFlags(const Value: TCPUIDFeatureFlagSet); +begin + if (FFeatureFlags <> Value) then + begin + FFeatureFlags := Value; + Changed; + end; +end; +{$ENDREGION} + +{$REGION 'Class: TX86Registers'} +procedure TX86Registers.AssignTo(Dest: TPersistent); +var + D: TX86Registers; +begin + if (Dest is TX86Registers) then + begin + D := Dest as TX86Registers; + D.SetRegisters(FRegisters); + end else inherited; +end; + +procedure TX86Registers.Changed; +begin + FDefinition.UpdateValues; +end; + +constructor TX86Registers.Create(Definition: TInstructionDefinition); +begin + inherited Create; + FDefinition := Definition; +end; + +function TX86Registers.Equals(const Value: TX86Registers): Boolean; +begin + Result := (Value.FRegisters = FRegisters); +end; + +procedure TX86Registers.LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); +var + A: PJSONVariantData; + I: Integer; + R: TX86Register; + Value: TX86RegisterSet; +begin + A := JSON^.Data(FieldName); + if (Assigned(A)) then + begin + if (A^.Kind <> jvArray) then + begin + raise Exception.CreateFmt('The "%s" field is not a valid JSON array.', [FieldName]); + end; + Value := []; + for I := 0 to A^.Count - 1 do + begin + for R := Low(SX86Register) to High(SX86Register) do + begin + if (LowerCase(A^.Item[I]) = SX86Register[R]) then + begin + Value := Value + [R]; + Break; + end; + end; + end; + SetRegisters(Value); + end; +end; + +procedure TX86Registers.SaveToJSON(JSON: PJSONVariantData; const FieldName: String); +var + A: TJSONVariantData; + R: TX86Register; +begin + A.Init; + for R in FRegisters do + begin + A.AddValue(SX86Register[R]); + end; + if (A.Count > 0) then + begin + JSON.AddNameValue(FieldName, Variant(A)); + end; +end; + +procedure TX86Registers.SetRegisters(const Value: TX86RegisterSet); +begin + if (FRegisters <> Value) then + begin + FRegisters := Value; + Changed; + end; +end; +{$ENDREGION} + +{$REGION 'Class: TX86Flags'} +procedure TX86Flags.AssignTo(Dest: TPersistent); +var + D: TX86Flags; +begin + if (Dest is TX86Flags) then + begin + D := Dest as TX86Flags; + D.FCF := FCF; + D.FPF := FPF; + D.FAF := FAF; + D.FZF := FZF; + D.FSF := FSF; + D.FTF := FTF; + D.FIF := FIF; + D.FDF := FDF; + D.FOF := FOF; + D.FRF := FRF; + D.FVM := FVM; + D.FAC := FAC; + D.FVIF := FVIF; + D.FVIP := FVIP; + D.FID := FID; + D.Changed; + end else inherited; +end; + +procedure TX86Flags.Changed; +begin + FDefinition.UpdateValues; +end; + +constructor TX86Flags.Create(Definition: TInstructionDefinition); +begin + inherited Create; + FDefinition := Definition; +end; + +function TX86Flags.Equals(const Value: TX86Flags): Boolean; +begin + Result := (Value.FCF = FCF) and (Value.FPF = FPF) and (Value.FAF = FAF) and + (Value.FZF = FZF) and (Value.FSF = FSF) and (Value.FTF = FTF) and (Value.FIF = FIF) and + (Value.FDF = FDF) and (Value.FOF = FOF) and (Value.FRF = FRF) and (Value.FVM = FVM) and + (Value.FAC = FAC) and (Value.FVIF = FVIF) and (Value.FVIP = FVIP) and (Value.FID = FID); +end; + +procedure TX86Flags.LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); +var + C: PJSONVariantData; + F: array[0..14] of ^TX86FlagValue; + N: array[0..14] of String; + I, J: Integer; +begin + C := JSON.Data(FieldName); + if (Assigned(C)) then + begin + if (C^.Kind <> jvObject) then + begin + raise Exception.CreateFmt('The "%s" field is not a valid JSON object.', [FieldName]); + end; + F[ 0] := @FCF; F[ 1] := @FPF; F[ 2] := @FAF; F[ 3] := @FZF; F[ 4] := @FSF; + F[ 5] := @FTF; F[ 6] := @FIF; F[ 7] := @FDF; F[ 8] := @FOF; F[ 9] := @FRF; + F[10] := @FVM; F[11] := @FAC; F[12] := @FVIF; F[13] := @FVIP; F[14] := @FID; + N[ 0] := 'cf'; N[ 1] := 'pf'; N[ 2] := 'af'; N[ 3] := 'zf'; N[ 4] := 'sf'; + N[ 5] := 'tf'; N[ 6] := 'if'; N[ 7] := 'df'; N[ 8] := 'of'; N[ 9] := 'rf'; + N[10] := 'vm'; N[11] := 'ac'; N[12] := 'vif'; N[13] := 'vip'; N[14] := 'id'; + for I := Low(N) to High(N) do + begin + J := TJSONEnumHelper.ReadEnumValueFromString(C, N[I], SX86FlagValue); + F[I]^ := TX86FlagValue(J); + end; + Changed; + end; +end; + +procedure TX86Flags.SaveToJSON(JSON: PJSONVariantData; const FieldName: String); +var + F: array[0..14] of ^TX86FlagValue; + N: array[0..14] of String; + J: TJSONVariantData; + I: Integer; +begin + F[ 0] := @FCF; F[ 1] := @FPF; F[ 2] := @FAF; F[ 3] := @FZF; F[ 4] := @FSF; + F[ 5] := @FTF; F[ 6] := @FIF; F[ 7] := @FDF; F[ 8] := @FOF; F[ 9] := @FRF; + F[10] := @FVM; F[11] := @FAC; F[12] := @FVIF; F[13] := @FVIP; F[14] := @FID; + N[ 0] := 'cf'; N[ 1] := 'pf'; N[ 2] := 'af'; N[ 3] := 'zf'; N[ 4] := 'sf'; + N[ 5] := 'tf'; N[ 6] := 'if'; N[ 7] := 'df'; N[ 8] := 'of'; N[ 9] := 'rf'; + N[10] := 'vm'; N[11] := 'ac'; N[12] := 'vif'; N[13] := 'vip'; N[14] := 'id'; + J.Init; + for I := Low(N) to High(N) do + begin + if (F[I]^ <> fvUnused) then + begin + J.AddNameValue(N[I], SX86FlagValue[F[I]^]); + end; + end; + if (J.Count > 0) then + begin + JSON.AddNameValue(FieldName, Variant(J)); + end; +end; + +procedure TX86Flags.SetAC(const Value: TX86FlagValue); +begin + if (FAC <> Value) then + begin + FAC := Value; + Changed; + end; +end; + +procedure TX86Flags.SetAF(const Value: TX86FlagValue); +begin + if (FAF <> Value) then + begin + FAF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetCF(const Value: TX86FlagValue); +begin + if (FCF <> Value) then + begin + FCF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetDF(const Value: TX86FlagValue); +begin + if (FDF <> Value) then + begin + FDF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetID(const Value: TX86FlagValue); +begin + if (FID <> Value) then + begin + FID := Value; + Changed; + end; +end; + +procedure TX86Flags.SetIF(const Value: TX86FlagValue); +begin + if (FIF <> Value) then + begin + FIF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetOF(const Value: TX86FlagValue); +begin + if (FOF <> Value) then + begin + FOF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetPF(const Value: TX86FlagValue); +begin + if (FPF <> Value) then + begin + FPF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetRF(const Value: TX86FlagValue); +begin + if (FRF <> Value) then + begin + FRF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetSF(const Value: TX86FlagValue); +begin + if (FSF <> Value) then + begin + FSF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetTF(const Value: TX86FlagValue); +begin + if (FTF <> Value) then + begin + FTF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetVIF(const Value: TX86FlagValue); +begin + if (FVIF <> Value) then + begin + FVIF := Value; + Changed; + end; +end; + +procedure TX86Flags.SetVIP(const Value: TX86FlagValue); +begin + if (FVIP <> Value) then + begin + FVIP := Value; + Changed; + end; +end; + +procedure TX86Flags.SetVM(const Value: TX86FlagValue); +begin + if (FVM <> Value) then + begin + FVM := Value; + Changed; + end; +end; + +procedure TX86Flags.SetZF(const Value: TX86FlagValue); +begin + if (FZF <> Value) then + begin + FZF := Value; + Changed; + end; +end; +{$ENDREGION} + +{$REGION 'Class: TInstructionOperand'} +procedure TInstructionOperand.AssignTo(Dest: TPersistent); +var + D: TInstructionOperand; +begin + if (Dest is TInstructionOperand) then + begin + D := Dest as TInstructionOperand; + D.FType := FType; + D.FEncoding := FEncoding; + D.FAccessMode := FAccessMode; + D.Changed; + end else inherited; +end; + +procedure TInstructionOperand.Changed; +begin + FOperands.Changed; +end; + +constructor TInstructionOperand.Create(Operands: TInstructionOperands); +begin + inherited Create; + FOperands := Operands; +end; + +function TInstructionOperand.Equals(const Value: TInstructionOperand): Boolean; +begin + Result := + (Value.FType = FType) and (Value.FEncoding = FEncoding) and (Value.FAccessMode = FAccessMode); +end; + +function TInstructionOperand.GetConflictState: Boolean; +begin + Result := false; + case FType of + optGPR8, + optGPR16: + Result := not (FEncoding in [opeModrmReg, opeModrmRm, opeOpcodeBits]); + optGPR32, + optGPR64: + Result := not (FEncoding in [opeModrmReg, opeModrmRm, opeOpcodeBits, opeVexVVVV]); + optFPR: + Result := not (FEncoding in [opeModrmRm]); + optVR64: + Result := not (FEncoding in [opeModrmReg, opeModrmRm]); + optVR128, + optVR256, + optVR512: + Result := not (FEncoding in [opeModrmReg, opeModrmRm, opeVexVVVV, opeImm8, opeModrmRmCD1, + opeModrmRmCD2, opeModrmRmCD4, opeModrmRmCD8, opeModrmRmCD16, opeModrmRmCD32, + opeModrmRmCD64]); + optCR, + optDR, + optSREG: + Result := not (FEncoding in [opeModrmReg]); + optMSKR: + Result := not (FEncoding in [opeModrmReg, opeModrmRm, opeVexVVVV]); + optBNDR: + Result := not (FEncoding in [opeModrmReg, opeModrmRm]); + optMem: + Result := not (FEncoding in [opeModrmRm]); + optMem8, + optMem16, + optMem32, + optMem64: + Result := not (FEncoding in [opeModrmRm, opeModrmRmCD1, opeModrmRmCD2, opeModrmRmCD4, + opeModrmRmCD8, opeModrmRmCD16, opeModrmRmCD32, opeModrmRmCD64]); + optMem80: + Result := not (FEncoding in [opeModrmRm]); + optMem128, + optMem256, + optMem512: + Result := not (FEncoding in [opeModrmRm, opeModrmRmCD1, opeModrmRmCD2, opeModrmRmCD4, + opeModrmRmCD8, opeModrmRmCD16, opeModrmRmCD32, opeModrmRmCD64]); + optMem32Bcst2, + optMem32Bcst4, + optMem32Bcst8, + optMem32Bcst16, + optMem64Bcst2, + optMem64Bcst4, + optMem64Bcst8, + optMem64Bcst16: + Result := not (FEncoding in [opeModrmRmCD1, opeModrmRmCD2, opeModrmRmCD4, opeModrmRmCD8, + opeModrmRmCD16, opeModrmRmCD32, opeModrmRmCD64]); + optMem32VSIBX, + optMem32VSIBY, + optMem32VSIBZ, + optMem64VSIBX, + optMem64VSIBY, + optMem64VSIBZ: + Result := not (FEncoding in [opeModrmRm, opeModrmRmCD1, opeModrmRmCD2, opeModrmRmCD4, + opeModrmRmCD8, opeModrmRmCD16, opeModrmRmCD32, opeModrmRmCD64]); + optMem1616, + optMem1632, + optMem1664, + optMem112, + optMem224: + Result := not (FEncoding in [opeModrmRm]); + optImm8, + optImm8U: + Result := not (FEncoding in [opeImm8]); + optImm16: + Result := not (FEncoding in [opeImm8, opeImm16]); + optImm32: + Result := not (FEncoding in [opeImm8, opeImm32]); + optImm64: + Result := not (FEncoding in [opeImm8, opeImm32, opeImm64]); + optRel8: + Result := not (FEncoding in [opeImm8]); + optRel16: + Result := not (FEncoding in [opeImm16]); + optRel32: + Result := not (FEncoding in [opeImm32]); + optRel64: + Result := not (FEncoding in [opeImm64]); + optPtr1616, + optPtr1632, + optPtr1664, + optMoffs16, + optMoffs32, + optMoffs64, + optSrcIndex8, + optSrcIndex16, + optSrcIndex32, + optSrcIndex64, + optDstIndex8, + optDstIndex16, + optDstIndex32, + optDstIndex64, + optFixed1, + optFixedAL, + optFixedCL, + optFixedAX, + optFixedDX, + optFixedEAX, + optFixedRAX, + optFixedST0, + optFixedES, + optFixedSS, + optFixedCS, + optFixedDS, + optFixedFS, + optFixedGS: + Result := not (FEncoding in [opeNone]); + end; +end; + +function TInstructionOperand.GetDescription(IncludeAccessMode: Boolean): String; +begin + if (GetConflictState) then + begin + Result := 'invalid'; + Exit; + end; + Result := ''; + if (FType <> optUnused) then + begin + case FType of + optGPR8 : Result := 'GPR8'; + optGPR16 : Result := 'GPR16'; + optGPR32 : Result := 'GPR32'; + optGPR64 : Result := 'GPR64'; + optFPR : Result := 'ST(i)'; + optVR64 : Result := 'MM64'; + optVR128 : Result := 'XMM128'; + optVR256 : Result := 'YMM256'; + optVR512 : Result := 'ZMM512'; + optMSKR : Result := 'MASK'; + optBNDR : Result := 'BND'; + optCR : Result := 'CR'; + optDR : Result := 'DR'; + optMem : Result := 'mem'; + optMem8 : Result := 'mem8'; + optMem16 : Result := 'mem16'; + optMem32 : Result := 'mem32'; + optMem64 : Result := 'mem64'; + optMem80 : Result := 'mem80'; + optMem128 : Result := 'mem128'; + optMem256 : Result := 'mem256'; + optMem512 : Result := 'mem512'; + optMem32Bcst2: Result := 'mem32bcst2'; + optMem32Bcst4: Result := 'mem32bcst4'; + optMem32Bcst8: Result := 'mem32bcst8'; + optMem32Bcst16: Result := 'mem32bcst16'; + optMem64Bcst2: Result := 'mem64bcst2'; + optMem64Bcst4: Result := 'mem64bcst4'; + optMem64Bcst8: Result := 'mem64bcst8'; + optMem64Bcst16: Result := 'mem64bcst16'; + optMem112 : Result := 'mem112'; + optMem224 : Result := 'mem224'; + optImm8 : Result := 'imm8'; + optImm16 : Result := 'imm16'; + optImm32 : Result := 'imm32'; + optImm64 : Result := 'imm64'; + optImm8U : Result := 'imm8u'; + optRel8 : Result := 'rel8'; + optRel16 : Result := 'rel16'; + optRel32 : Result := 'rel32'; + optRel64 : Result := 'rel64'; + optPtr1616 : Result := 'ptr16:16'; + optPtr1632 : Result := 'ptr16:32'; + optPtr1664 : Result := 'ptr16:64'; + optMoffs16 : Result := 'moffs16'; + optMoffs32 : Result := 'moffs32'; + optMoffs64 : Result := 'moffs64'; + optSrcIndex8 : Result := 'srcidx8'; + optSrcIndex16: Result := 'srcidx16'; + optSrcIndex32: Result := 'srcidx32'; + optSrcIndex64: Result := 'srcidx64'; + optDstIndex8 : Result := 'dstidx8'; + optDstIndex16: Result := 'dstidx16'; + optDstIndex32: Result := 'dstidx32'; + optDstIndex64: Result := 'dstidx64'; + optSREG : Result := 'SEG'; + optMem1616 : Result := 'mem16:16'; + optMem1632 : Result := 'mem16:32'; + optMem1664 : Result := 'mem16:64'; + optMem32VSIBX : Result := 'mem32vsibx'; + optMem32VSIBY : Result := 'mem32vsiby'; + optMem32VSIBZ : Result := 'mem32vsibz'; + optMem64VSIBX : Result := 'mem64vsibx'; + optMem64VSIBY : Result := 'mem64vsiby'; + optMem64VSIBZ : Result := 'mem64vsibz'; + optFixed1 : Result := '1'; + optFixedAL : Result := 'AL'; + optFixedCL : Result := 'CL'; + optFixedAX : Result := 'AX'; + optFixedDX : Result := 'DX'; + optFixedEAX : Result := 'EAX'; + optFixedRAX : Result := 'RAX'; + optFixedES : Result := 'ES'; + optFixedCS : Result := 'CS'; + optFixedSS : Result := 'SS'; + optFixedDS : Result := 'DS'; + optFixedGS : Result := 'GS'; + optFixedFS : Result := 'FS'; + optFixedST0 : Result := 'ST0'; + end; + if (IncludeAccessMode) then + begin + case FAccessMode of + opaRead : Result := Result + ' (r)'; + opaWrite : Result := Result + ' (w)'; + opaReadWrite: Result := Result + ' (r, w)'; + end; + end; + end; +end; + +procedure TInstructionOperand.LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); +var + V: PJSONVariantData; + I: Integer; +begin + V := JSON^.Data(FieldName); + if Assigned(V) then + begin + if (V^.Kind <> jvObject) then + begin + raise Exception.CreateFmt('The "%s" field is not a valid JSON object.', [FieldName]); + end; + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'type', SOperandType); + SetType(TOperandType(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'encoding', SOperandEncoding); + SetEncoding(TOperandEncoding(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(V, 'accessmode', SOperandAccessMode); + SetAccessMode(TOperandAccessMode(I)); + end; +end; + +procedure TInstructionOperand.SaveToJSON(JSON: PJSONVariantData; const FieldName: String); +var + V: TJSONVariantData; +begin + if (FType <> optUnused) then + begin + V.Init; + V.AddNameValue('type', SOperandType[FType]); + if (FEncoding <> opeNone) then V.AddNameValue('encoding', SOperandEncoding[FEncoding]); + if (FAccessMode <> opaRead) then V.AddNameValue('accessmode', SOperandAccessMode[FAccessMode]); + JSON^.AddNameValue(FieldName, Variant(V)); + end; +end; + +procedure TInstructionOperand.SetAccessMode(const Value: TOperandAccessMode); +begin + if (FAccessMode <> Value) then + begin + FAccessMode := Value; + Changed; + end; +end; + +procedure TInstructionOperand.SetEncoding(const Value: TOperandEncoding); +begin + if (FEncoding <> Value) then + begin + FEncoding := Value; + Changed; + end; +end; + +procedure TInstructionOperand.SetType(const Value: TOperandType); +begin + if (FType <> Value) then + begin + case Value of + optGPR8: ; + optGPR16: ; + optGPR32: ; + optGPR64: ; + optVR64: ; + optVR128: ; + optVR256: ; + optVR512: ; + optFPR: + FEncoding := opeModrmRm; + optCR: ; + optDR: ; + optSREG: ; + optMSKR: ; + optBNDR: ; + optMem: ; + optMem8: ; + optMem16: ; + optMem32: ; + optMem64: ; + optMem80: ; + optMem128: ; + optMem256: ; + optMem512: ; + optMem32Bcst2: ; + optMem32Bcst4: ; + optMem32Bcst8: ; + optMem32Bcst16: ; + optMem64Bcst2: ; + optMem64Bcst4: ; + optMem64Bcst8: ; + optMem64Bcst16: ; + optMem32VSIBX: ; + optMem32VSIBY: ; + optMem32VSIBZ: ; + optMem64VSIBX: ; + optMem64VSIBY: ; + optMem64VSIBZ: ; + optMem1616: ; + optMem1632: ; + optMem1664: ; + optMem112: ; + optMem224: ; + optImm8: ; + optImm8U: ; + optImm16: ; + optImm32: ; + optImm64: ; + optRel8: ; + optRel16: ; + optRel32: ; + optRel64: ; + optPtr1616: ; + optPtr1632: ; + optPtr1664: ; + optMoffs16: ; + optMoffs32: ; + optMoffs64: ; + optSrcIndex8: ; + optSrcIndex16: ; + optSrcIndex32: ; + optSrcIndex64: ; + optDstIndex8: ; + optDstIndex16: ; + optDstIndex32: ; + optDstIndex64: ; + optFixed1: ; + optFixedAL: ; + optFixedCL: ; + optFixedAX: ; + optFixedDX: ; + optFixedEAX: ; + optFixedRAX: ; + optFixedST0: ; + optFixedES: ; + optFixedSS: ; + optFixedCS: ; + optFixedDS: ; + optFixedFS: ; + optFixedGS: ; + end; + {case Value of + optUnused: + FEncoding := opeNone; + optGPR8, + optGPR16, + optGPR32, + optGPR64, + optVR64, + optVR128, + optVR256, + optVR512, + optBNDR, + optCR, + optDR: + if (FEncoding <> opeModrmRm) then + begin + FEncoding := opeModrmReg; + end; + optMSKR: + if (FEncoding <> opeModrmRm) and (FEncoding <> opeVexVVVV) then + begin + FEncoding := opeModrmReg; + end; + optFPR: + FEncoding := opeModrmRm; + optMem, + optMem8, + optMem16, + optMem32, + optMem64, + optMem80, + optMem128, + optMem256, + optMem512, + optMem1616, + optMem1632, + optMem1664, + optMem32VSIBX, + optMem32VSIBY, + optMem32VSIBZ, + optMem64VSIBX, + optMem64VSIBY, + optMem64VSIBZ, + optMem32Bcst2, + optMem32Bcst4, + optMem32Bcst8, + optMem32Bcst16, + optMem64Bcst2, + optMem64Bcst4, + optMem64Bcst8, + optMem64Bcst16, + optMem112, + optMem224: + if not (FEncoding in [opeModrmRm, opeModrmRmCD1, opeModrmRmCD2, opeModrmRmCD4, + opeModrmRmCD8, opeModrmRmCD16, opeModrmRmCD32, opeModrmRmCD64]) then + begin + FEncoding := opeModrmRm; + end; + optImm8: + FEncoding := opeImm8; + optImm16: + FEncoding := opeImm16; + optImm32: + FEncoding := opeImm32; + optImm64: + if (FEncoding <> opeImm32) then + begin + FEncoding := opeImm64; + end; + optImm8U: + FEncoding := opeImm8; + optRel8: + FEncoding := opeImm8; + optRel16: + FEncoding := opeImm16; + optRel32: + FEncoding := opeImm32; + optRel64: + if (FEncoding <> opeImm32) then // TODO: ? + begin + FEncoding := opeImm64; + end; + optPtr1616, + optPtr1632, + optPtr1664: + FEncoding := opeNone; + optMoffs16, + optMoffs32, + optMoffs64: + FEncoding := opeNone; + optSREG: + FEncoding := opeModrmReg; + optSrcIndex8, + optSrcIndex16, + optSrcIndex32, + optSrcIndex64, + optDstIndex8, + optDstIndex16, + optDstIndex32, + optDstIndex64, + optFixed1, + optFixedAL, + optFixedCL, + optFixedAX, + optFixedDX, + optFixedEAX, + optFixedRAX, + optFixedCS, + optFixedSS, + optFixedDS, + optFixedES, + optFixedFS, + optFixedGS, + optFixedST0: + FEncoding := opeNone; + end;} + FType := Value; + Changed; + end; +end; +{$ENDREGION} + +{$REGION 'Class: TInstructionOperands'} +procedure TInstructionOperands.AssignTo(Dest: TPersistent); +var + D: TInstructionOperands; +begin + if (Dest is TInstructionOperands) then + begin + D := Dest as TInstructionOperands; + D.FOperandA.Assign(FOperandA); + D.FOperandB.Assign(FOperandB); + D.FOperandC.Assign(FOperandC); + D.FOperandD.Assign(FOperandD); + D.Changed; + end else inherited; +end; + +procedure TInstructionOperands.Changed; +{var + A: array[0..3] of TInstructionOperand; + I, J: Integer; +begin + FHasConflicts := false; + + // Check for invalid operand order + A[0] := FOperandA; A[1] := FOperandB; A[2] := FOperandC; A[3] := FOperandD; + for I := High(A) downto Low(A) do + begin + if (A[I].OperandType <> otUnused) then + begin + for J := I downto Low(A) do + begin + if (A[J].OperandType = otUnused) then + begin + FHasConflicts := true; + Break; + end; + end; + end; + if (FHasConflicts) then + begin + Break; + end; + end; } +begin + + // TODO: Determine Encoding + + FDefinition.UpdateValues; +end; + +constructor TInstructionOperands.Create(Definition: TInstructionDefinition); +begin + inherited Create; + FDefinition := Definition; + FOperandA := TInstructionOperand.Create(Self); + FOperandB := TInstructionOperand.Create(Self); + FOperandC := TInstructionOperand.Create(Self); + FOperandD := TInstructionOperand.Create(Self); +end; + +destructor TInstructionOperands.Destroy; +begin + if (Assigned(FOperandA)) then FOperandA.Free; + if (Assigned(FOperandB)) then FOperandB.Free; + if (Assigned(FOperandC)) then FOperandC.Free; + if (Assigned(FOperandD)) then FOperandD.Free; + inherited; +end; + +function TInstructionOperands.Equals(const Value: TInstructionOperands): Boolean; +begin + Result := + (Value.FOperandA.Equals(FOperandA)) and (Value.FOperandB.Equals(FOperandB)) and + (Value.FOperandC.Equals(FOperandC)) and (Value.FOperandD.Equals(FOperandD)); +end; + +function TInstructionOperands.GetConflictState: Boolean; +var + I: Integer; + EncReg, EncRm, EncVVVV, EncAAA: Integer; +begin + Result := + (FOperandA.HasConflicts) or (FOperandB.HasConflicts) or (FOperandC.HasConflicts) or + (OperandD.HasConflicts); + if (not Result) then + begin + EncReg := 0; EncRm := 0; EncVVVV := 0; EncAAA := 0; + for I := 0 to 3 do + begin + case GetOperandById(I).Encoding of + opeModrmReg : Inc(EncReg); + opeModrmRm, + opeModrmRmCD1, + opeModrmRmCD2, + opeModrmRmCD4, + opeModrmRmCD8, + opeModrmRmCD16, + opeModrmRmCD32, + opeModrmRmCD64: Inc(EncRm); + opeVexVVVV : Inc(EncVVVV); + opeEvexAAA : Inc(EncAAA); + end; + if (EncReg > 1) or (EncRm > 1) or (EncVVVV > 1) or (EncAAA > 1) then + begin + Result := true; + Break; + end; + end; + if (FDefinition.OpcodeExtensions.ModrmMod <> mdNeutral) and (EncReg > 0) and (EncRm = 0) then + begin + Exit(true) + end + // TODO: Check operand order, ... + end; +end; + +function TInstructionOperands.GetOperandById(Id: Integer): TInstructionOperand; +begin + Result := nil; + case Id of + 0: Result := FOperandA; + 1: Result := FOperandB; + 2: Result := FOperandC; + 3: Result := FOperandD; + end; +end; + +procedure TInstructionOperands.LoadFromJSON(JSON: PJSONVariantData; const FieldName: String); +var + V: PJSONVariantData; +begin + V := JSON^.Data(FieldName); + if Assigned(V) then + begin + if (V^.Kind <> jvObject) then + begin + raise Exception.CreateFmt('The "%s" field is not a valid JSON object.', [FieldName]); + end; + FOperandA.LoadFromJSON(V, 'operand1'); + FOperandB.LoadFromJSON(V, 'operand2'); + FOperandC.LoadFromJSON(V, 'operand3'); + FOperandD.LoadFromJSON(V, 'operand4'); + end; +end; + +procedure TInstructionOperands.SaveToJSON(JSON: PJSONVariantData; const FieldName: String); +var + V: TJSONVariantData; +begin + V.Init; + FOperandA.SaveToJSON(@V, 'operand1'); + FOperandB.SaveToJSON(@V, 'operand2'); + FOperandC.SaveToJSON(@V, 'operand3'); + FOperandD.SaveToJSON(@V, 'operand4'); + if (V.Count > 0) then + begin + JSON^.AddNameValue('operands', Variant(V)); + end; +end; +{$ENDREGION} + +{$REGION 'Class: TInstructionDefinition'} +procedure TInstructionDefinition.AssignTo(Dest: TPersistent); +var + D: TInstructionDefinition; +begin + if (Dest is TInstructionDefinition) then + begin + D := Dest as TInstructionDefinition; + D.BeginUpdate; + try + D.FMnemonic := FMnemonic; + D.FEncoding := FEncoding; + D.FOpcodeMap := FOpcodeMap; + D.FOpcode := FOpcode; + D.FExtensions.Assign(FExtensions); + D.FCPUID.Assign(FCPUID); + D.FOperands.Assign(FOperands); + D.FFlags := FFlags; + D.FImplicitRead.Assign(FImplicitRead); + D.FImplicitWrite.Assign(FImplicitWrite); + D.FX86Flags.Assign(FX86Flags); + D.FComment := FComment; + D.Update; + finally + D.EndUpdate; + end; + end else inherited; +end; + +procedure TInstructionDefinition.BeginUpdate; +begin + Inc(FUpdateCount); +end; + +constructor TInstructionDefinition.Create(Editor: TInstructionEditor; const Mnemonic: String); +begin + inherited Create; + FEditor := Editor; + if (Mnemonic = '') then + begin + raise Exception.Create('Mnemonic can not be empty.'); + end; + FMnemonic := Mnemonic; + FExtensions := TOpcodeExtensions.Create(Self); + FCPUID := TCPUIDFeatureFlags.Create(Self); + FOperands := TInstructionOperands.Create(Self); + FImplicitRead := TX86Registers.Create(Self); + FImplicitWrite := TX86Registers.Create(Self); + FX86Flags := TX86Flags.Create(Self); + // Insert definition into the definition list. This method does NOT insert the definition into + // the table structure + FEditor.RegisterDefinition(Self); +end; + +destructor TInstructionDefinition.Destroy; +begin + // Remove definition from the filter structure + if (Assigned(FParent)) then + begin + FEditor.RemoveDefinition(Self); + end; + // Remove definition from the definition list + FEditor.UnregisterDefinition(Self); + if (Assigned(FExtensions)) then FExtensions.Free; + if (Assigned(FCPUID)) then FCPUID.Free; + if (Assigned(FOperands)) then FOperands.Free; + if (Assigned(FImplicitRead)) then FImplicitRead.Free; + if (Assigned(FImplicitWrite)) then FImplicitWrite.Free; + if (Assigned(FX86Flags)) then FX86Flags.Free; + inherited; +end; + +procedure TInstructionDefinition.EndUpdate; +begin + if (FUpdateCount > 0) then + begin + Dec(FUpdateCount); + end; + if (FUpdateCount = 0) then + begin + if (FDoUpdatePosition) then + begin + UpdatePosition; + FDoUpdatePosition := false; + end; + if (FDoUpdateValues) then + begin + UpdateValues; + FDoUpdateValues := false; + end; + end; +end; + +function TInstructionDefinition.Equals(const Value: TInstructionDefinition): Boolean; +begin + // Comment is excluded from the equality check + Result := + (Value.FMnemonic = FMnemonic) and (Value.FEncoding = FEncoding) and + (Value.FOpcodeMap = FOpcodeMap) and (Value.FOpcode = FOpcode) and + (Value.FExtensions.Equals(FExtensions)) and (Value.FCPUID.Equals(FCPUID)) and + (Value.FOperands.Equals(FOperands)) and (Value.FFlags = FFlags) and + (Value.FImplicitRead.Equals(FImplicitRead)) and + (Value.FImplicitWrite.Equals(FImplicitWrite)) and (Value.FX86Flags.Equals(FX86Flags)); +end; + +function TInstructionDefinition.GetConflictState: Boolean; +begin + Result := (FConflicts <> []); +end; + +procedure TInstructionDefinition.LoadFromJSON(JSON: PJSONVariantData); +var + I: Integer; + A: PJSONVariantData; + F: TInstructionDefinitionFlag; + Flags: TInstructionDefinitionFlags; +begin + BeginUpdate; + try + if (VarIsClear(JSON^.Value['mnemonic']) or (JSON^.Value['mnemonic'] = '')) then + begin + raise Exception.Create('The "mnemonic" field can not be empty.'); + end; + SetMnemonic(JSON^.Value['mnemonic']); + I := TJSONEnumHelper.ReadEnumValueFromString(JSON, 'encoding', SInstructionEncoding); + SetEncoding(TInstructionEncoding(I)); + I := TJSONEnumHelper.ReadEnumValueFromString(JSON, 'map', SOpcodeMap); + SetOpcodeMap(TOpcodeMap(I)); + if (VarIsClear(JSON^.Value['opcode']) or + (not TryStrToInt('$' + JSON^.Value['opcode'], I))) or (I < 0) or (I >= 256) then + begin + raise Exception.Create('The "opcode" field does not contain a valid hexadecimal byte value.'); + end; + SetOpcode(I); + + FEVEXCD8Scale := JSON^.Value['cd8scale']; + + FExtensions.LoadFromJSON(JSON, 'extensions'); + FCPUID.LoadFromJSON(JSON, 'cpuid'); + FOperands.LoadFromJSON(JSON, 'operands'); + FImplicitRead.LoadFromJSON(JSON, 'implicit_read'); + FImplicitWrite.LoadFromJSON(JSON, 'implicit_write'); + A := JSON.Data('flags'); + if (Assigned(A)) then + begin + if (A^.Kind <> jvArray) then + begin + raise Exception.Create('The "flags" field is not a valid JSON array.'); + end; + Flags := []; + for I := 0 to A^.Count - 1 do + begin + for F := Low(SInstructionDefinitionFlag) to High(SInstructionDefinitionFlag) do + begin + if (LowerCase(A^.Item[I]) = SInstructionDefinitionFlag[F]) then + begin + Flags := Flags + [F]; + Break; + end; + end; + end; + SetFlags(Flags); + end; + FX86Flags.LoadFromJSON(JSON, 'x86flags'); + FComment := JSON^.Value['comment']; + finally + EndUpdate; + end; +end; + +procedure TInstructionDefinition.SaveToJSON(JSON: PJSONVariantData); +var + A: TJSONVariantData; + F: TInstructionDefinitionFlag; +begin + JSON^.AddNameValue('mnemonic', FMnemonic); + JSON^.AddNameValue('opcode', LowerCase(IntToHex(FOpcode, 2))); + if (FEncoding <> ieDefault) then JSON^.AddNameValue('encoding', SInstructionEncoding[FEncoding]); + if (FOpcodeMap <> omDefault) then JSON^.AddNameValue('map', SOpcodeMap[FOpcodeMap]); + FExtensions.SaveToJSON(JSON, 'extensions'); + FCPUID.SaveToJSON(JSON, 'cpuid'); + FOperands.SaveToJSON(JSON, 'operands'); + FImplicitRead.SaveToJSON(JSON, 'implicit_read'); + FImplicitWrite.SaveToJSON(JSON, 'implicit_write'); + A.Init; + for F in FFlags do + begin + A.AddValue(SInstructionDefinitionFlag[F]); + end; + if (A.Count > 0) then + begin + JSON^.AddNameValue('flags', Variant(A)); + end; + FX86Flags.SaveToJSON(JSON, 'x86flags'); + if (FComment <> '') then + begin + JSON^.AddNameValue('comment', FComment); + end; + JSON^.AddNameValue('cd8scale', FEVEXCD8Scale); +end; + +procedure TInstructionDefinition.SetComment(const Value: String); +begin + if (FComment <> Value) then + begin + FComment := Value; + UpdateValues; + end; +end; + +procedure TInstructionDefinition.SetEncoding(const Value: TInstructionEncoding); +begin + if (FEncoding <> Value) then + begin + // TODO: Check exception cases + case Value of + ieDefault, + ieVEX, + ieEVEX: + begin + if (not (FOpcodeMap in [omDefault, om0F, om0F38, om0F3A])) then FOpcodeMap := omDefault; + end; + ie3DNow: + begin + if (FOpcodeMap <> om0F) then FOpcodeMap := om0F; + end; + ieXOP: + begin + if (not (FOpcodeMap in [omXOP8, omXOP9, omXOPA])) then FOpcodeMap := omXOP8; + end; + end; + FEncoding := Value; + UpdatePosition; + end; +end; + +procedure TInstructionDefinition.SetFlags(const Value: TInstructionDefinitionFlags); +begin + if (FFlags <> Value) then + begin + FFlags := Value; + UpdateValues; + end; +end; + +procedure TInstructionDefinition.SetMnemonic(const Value: String); +begin + if (Value = '') then + begin + raise Exception.Create('Mnemonic can not be empty.'); + end; + if (FMnemonic <> Value) then + begin + FMnemonic := LowerCase(Value); + UpdateValues; + end; +end; + +procedure TInstructionDefinition.SetOpcode(const Value: TOpcodeByte); +begin + if (FOpcode <> Value) then + begin + FOpcode := Value; + UpdatePosition; + end; +end; + +procedure TInstructionDefinition.SetOpcodeMap(const Value: TOpcodeMap); +var + E: Boolean; +begin + if (FOpcodeMap <> Value) then + begin + E := false; + case FEncoding of + ieDefault, + ieVEX, + ieEVEX : E := (Value in [omXOP8, omXOP9, omXOPA]); + ie3DNow : E := (Value <> om0F); + ieXOP : E := (Value in [omDefault, om0F, om0F38, om0F3A]); + end; + if (E) then + begin + raise Exception.Create('The current instruction encoding does not support this opcode map.'); + end; + FOpcodeMap := Value; + UpdatePosition; + end; +end; + +procedure TInstructionDefinition.SetParent(Parent: TDefinitionContainer); +begin + // This method should ONLY be called by TInstructionDefinition.Create, + // TInstructionFilter.InsertDefinition and TInstructionFilter.RemoveDefinition + if (Assigned(FParent)) then + begin + if (HasConflicts) then + begin + FParent.DecInheritedConflictCount; + end; + FEditor.DefinitionRemoved(Self); + end; + FParent := Parent; + if (Assigned(Parent)) then + begin + if (HasConflicts) then + begin + FParent.IncInheritedConflictCount; + end; + FEditor.DefinitionInserted(Self); + end; +end; + +procedure TInstructionDefinition.Update; +begin + UpdatePosition; + UpdateValues; +end; + +procedure TInstructionDefinition.UpdateConflictFlags; +var + Conflicts: TInstructionDefinitionConflicts; +begin + Conflicts := []; + if (ifForceConflict in FFlags) then + begin + Include(Conflicts, idcForcedConflict); + end; + if (FOperands.HasConflicts) then + begin + Include(Conflicts, idcOperands); + end; + // TODO: Check for X86Flag conflicts + // [ ] EFLAGS in ImplicitRead / ImplicitWrite required or forbidden + // TODO: Check for more conflicts + if (FConflicts <> Conflicts) then + begin + if (Assigned(FParent)) then + begin + if (FConflicts = []) and (Conflicts <> []) then + begin + FParent.IncInheritedConflictCount; + end else if (FConflicts <> []) and (Conflicts = []) then + begin + FParent.DecInheritedConflictCount; + end; + end; + FConflicts := Conflicts; + end; +end; + +procedure TInstructionDefinition.UpdatePosition; +begin + UpdateValues; + if (FUpdateCount > 0) then + begin + FDoUpdatePosition := true; + end else + begin + FEditor.InsertDefinition(Self); + end; +end; + +procedure TInstructionDefinition.UpdateValues; +begin + if (FUpdateCount > 0) then + begin + FDoUpdateValues := true; + end else + begin + UpdateConflictFlags; + FEditor.DefinitionChanged(Self); + end; +end; +{$ENDREGION} + +{$REGION 'Class: TInstructionFilter'} +procedure TInstructionFilter.Changed; +begin + // TODO: Implement BeginUpdate, EndUpdate to reduce Changed calls + FEditor.FilterChanged(Self); +end; + +constructor TInstructionFilter.Create(Editor: TInstructionEditor; Parent: TInstructionFilter; + IsRootTable, IsStaticFilter: Boolean); +begin + inherited Create; + + Assert(Assigned(Editor)); + Assert((not Assigned(Parent)) or + (Assigned(Parent) and IsStaticFilter and (iffIsStaticFilter in Parent.FilterFlags)) or + (Assigned(Parent) and (not IsStaticFilter))); + + FEditor := Editor; + if (IsRootTable) then + begin + FFilterFlags := FFilterFlags + [iffIsRootTable]; + end; + if (IsStaticFilter) then + begin + FFilterFlags := FFilterFlags + [iffIsStaticFilter]; + end; + if (IsDefinitionContainer) then + begin + FFilterFlags := FFilterFlags + [iffIsDefinitionContainer]; + FDefinitions := TList.Create; + end else + begin + SetLength(FItems, GetCapacity); + end; + FEditor.FilterCreated(Self); + SetParent(Parent); +end; + +procedure TInstructionFilter.CreateFilterAtIndex(Index: Integer; + FilterClass: TInstructionFilterClass; IsRootTable, IsStaticFilter: Boolean); +begin + SetItem(Index, FilterClass.Create(FEditor, Self, IsRootTable, IsStaticFilter)); +end; + +procedure TInstructionFilter.DecInheritedConflictCount; +begin + Dec(FInheritedConflicts); + if (FInheritedConflicts = 0) then + begin + SetConflicts(FConflicts - [ifcInheritedConflict]); + if (Assigned(FParent)) then + begin + FParent.DecInheritedConflictCount; + end; + end; +end; + +destructor TInstructionFilter.Destroy; +begin + Assert((FItemCount = 0) and (FParent = nil)); + if Assigned(FDefinitions) then + begin + Assert(FDefinitions.Count = 0); + FDefinitions.Free; + end; + FEditor.FilterDestroyed(Self); + inherited; +end; + +class function TInstructionFilter.GetCapacity: Cardinal; +begin + Result := 0; +end; + +function TInstructionFilter.GetConflictState: Boolean; +begin + Result := (FConflicts <> []); +end; + +function TInstructionFilter.GetDefinition(const Index: Integer): TInstructionDefinition; +begin + Assert((Index >= 0) and (Index < FDefinitions.Count)); + Result := FDefinitions[Index]; +end; + +function TInstructionFilter.GetDefinitionCount: Integer; +begin + Result := 0; + if Assigned(FDefinitions) then + begin + Result := FDefinitions.Count; + end; +end; + +class function TInstructionFilter.GetDescription: String; +begin + Result := Self.ClassName; +end; + +class function TInstructionFilter.GetInsertPosition( + const Definition: TInstructionDefinition): Integer; +begin + Result := -1; +end; + +function TInstructionFilter.GetItem(const Index: Integer): TInstructionFilter; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := FItems[Index]; +end; + +class function TInstructionFilter.GetItemDescription(Index: Integer): String; +begin + Result := ''; +end; + +class function TInstructionFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netNotAvailable; +end; + +procedure TInstructionFilter.IncInheritedConflictCount; +begin + Inc(FInheritedConflicts); + if (FInheritedConflicts = 1) then + begin + SetConflicts(FConflicts + [ifcInheritedConflict]); + if (Assigned(FParent)) then + begin + FParent.IncInheritedConflictCount; + end; + end; +end; + +function TInstructionFilter.IndexOf(const Filter: TInstructionFilter): Integer; +var + I: Integer; +begin + Result := -1; + for I := Low(FItems) to High(FItems) do + begin + if (FItems[I] = Filter) then + begin + Result := I; + Break; + end; + end; +end; + +procedure TInstructionFilter.InsertDefinition(Definition: TInstructionDefinition); +begin + Assert(IsDefinitionContainer); + FDefinitions.Add(Definition); + Definition.SetParent(Self as TDefinitionContainer); + if (FDefinitions.Count = 2) then + begin + SetConflicts(FConflicts + [ifcDefinitionCount]); + if (Assigned(FParent)) then + begin + FParent.IncInheritedConflictCount; + end; + end; + Changed; +end; + +class function TInstructionFilter.IsDefinitionContainer: Boolean; +begin + Result := false; +end; + +procedure TInstructionFilter.RemoveDefinition(Definition: TInstructionDefinition); +begin + Assert(IsDefinitionContainer); + Assert(FDefinitions.IndexOf(Definition) >= 0); + if (FDefinitions.Count = 2) then + begin + SetConflicts(FConflicts - [ifcDefinitionCount]); + if (Assigned(FParent)) then + begin + FParent.DecInheritedConflictCount; + end; + end; + Definition.SetParent(nil); + FDefinitions.Remove(Definition); + Changed; +end; + +procedure TInstructionFilter.SetConflicts(const Value: TInstructionFilterConflicts); +begin + if (FConflicts <> Value) then + begin + FConflicts := Value; + Changed; + end; +end; + +procedure TInstructionFilter.SetItem(const Index: Integer; const Value: TInstructionFilter); +begin + Assert((Index >= 0) and (Index < Length(FItems))); + if (FItems[Index] <> Value) then + begin + if (Assigned(Value) and (not Assigned(FItems[Index]))) then + begin + Inc(FItemCount); + end else if (not Assigned(Value) and (Assigned(FItems[Index]))) then + begin + Dec(FItemCount); + end; + if (Assigned(FItems[Index])) then + begin + FItems[Index].SetParent(nil); + end; + FItems[Index] := Value; + if (Assigned(Value)) then + begin + FItems[Index].SetParent(Self); + end; + // Update neutral element conflict + if (GetNeutralElementType in [netPlaceholder]) then + begin + if (Assigned(FItems[0])) and (FItemCount > 1) then + begin + if (not (ifcNeutralElement in FConflicts)) then + begin + SetConflicts(FConflicts + [ifcNeutralElement]); + if (Assigned(FParent)) then + begin + FParent.IncInheritedConflictCount; + end; + end; + end else + begin + if (ifcNeutralElement in FConflicts) then + begin + SetConflicts(FConflicts - [ifcNeutralElement]); + if (Assigned(FParent)) then + begin + FParent.DecInheritedConflictCount; + end; + end; + end; + end; + Changed; + end; +end; + +procedure TInstructionFilter.SetParent(Parent: TInstructionFilter); +begin + Assert((not Assigned(Parent)) or + (Assigned(Parent) and (iffIsStaticFilter in FFilterFlags) and + (iffIsStaticFilter in Parent.FilterFlags)) or (Assigned(Parent) and + (not (iffIsStaticFilter in FFilterFlags)))); + if (FParent <> Parent) then + begin + if (Assigned(FParent)) then + begin + FEditor.FilterRemoved(Self); + if (HasConflicts) then + begin + FParent.DecInheritedConflictCount; + end; + end; + FParent := Parent; + if (Assigned(Parent)) then + begin + FEditor.FilterInserted(Self); + if (HasConflicts) then + begin + Parent.IncInheritedConflictCount; + end; + end; + Changed; + end; +end; +{$ENDREGION} + +{$REGION 'Class: TDefinitionContainer'} +class function TDefinitionContainer.IsDefinitionContainer: Boolean; +begin + Result := true; +end; +{$ENDREGION} + +{$REGION 'Class: TInstructionEditor'} +procedure TInstructionEditor.BeginUpdate; +begin + Inc(FUpdateCount); + if (FUpdateCount = 1) and Assigned(FOnBeginUpdate) then + begin + FOnBeginUpdate(Self); + end; +end; + +constructor TInstructionEditor.Create; +begin + inherited Create; + FDefinitions := TList.Create; +end; + +class constructor TInstructionEditor.Create; +begin + // Default filter order + SetLength(FilterOrderDef, 8); + FilterOrderDef[ 0] := TModrmModFilter; + FilterOrderDef[ 1] := TModrmRegFilter; + FilterOrderDef[ 2] := TModrmRmFilter; + FilterOrderDef[ 3] := TRexWFilter; + FilterOrderDef[ 4] := TOperandSizeFilter; + FilterOrderDef[ 5] := TAddressSizeFilter; + FilterOrderDef[ 6] := TMandatoryPrefixFilter; + FilterOrderDef[ 7] := TModeFilter; + // Specialized filter order for XOP instruction encoding + SetLength(FilterOrderXOP, 8); + FilterOrderXOP[ 0] := TModrmModFilter; + FilterOrderXOP[ 1] := TModrmRegFilter; + FilterOrderXOP[ 2] := TModrmRmFilter; + FilterOrderXOP[ 3] := TRexWFilter; + FilterOrderXOP[ 4] := TOperandSizeFilter; + FilterOrderXOP[ 5] := TAddressSizeFilter; + FilterOrderXOP[ 6] := TModeFilter; + FilterOrderXOP[ 7] := TVexLFilter; + // Specialized filter order for VEX instruction encoding + SetLength(FilterOrderVEX, 9); + FilterOrderVEX[ 0] := TOpcodeFilter; + FilterOrderVEX[ 1] := TModrmModFilter; + FilterOrderVEX[ 2] := TModrmRegFilter; + FilterOrderVEX[ 3] := TModrmRmFilter; + FilterOrderVEX[ 4] := TRexWFilter; + FilterOrderVEX[ 5] := TOperandSizeFilter; + FilterOrderVEX[ 6] := TAddressSizeFilter; + FilterOrderVEX[ 7] := TModeFilter; + FilterOrderVEX[ 8] := TVexLFilter; + // Specialized filter order for EVEX instruction encoding + SetLength(FilterOrderEVEX, 11); + FilterOrderEVEX[ 0] := TOpcodeFilter; + FilterOrderEVEX[ 1] := TModrmModFilter; + FilterOrderEVEX[ 2] := TModrmRegFilter; + FilterOrderEVEX[ 3] := TModrmRmFilter; + FilterOrderEVEX[ 4] := TRexWFilter; + FilterOrderEVEX[ 5] := TOperandSizeFilter; + FilterOrderEVEX[ 6] := TAddressSizeFilter; + FilterOrderEVEX[ 7] := TModeFilter; + FilterOrderEVEX[ 8] := TEvexBFilter; + FilterOrderEVEX[ 9] := TVexLFilter; + FilterOrderEVEX[10] := TEvexL2Filter; +end; + +function TInstructionEditor.CreateDefinition(const Mnemonic: String): TInstructionDefinition; +begin + Result := TInstructionDefinition.Create(Self, Mnemonic); +end; + +procedure TInstructionEditor.DefinitionChanged(Definition: TInstructionDefinition); +begin + if Assigned(FOnDefinitionChanged) then + begin + FOnDefinitionChanged(Self, Definition); + end; +end; + +procedure TInstructionEditor.DefinitionInserted(Definition: TInstructionDefinition); +begin + if Assigned(FOnDefinitionInserted) then + begin + FOnDefinitionInserted(Self, Definition); + end; +end; + +procedure TInstructionEditor.DefinitionRemoved(Definition: TInstructionDefinition); +begin + if Assigned(FOnDefinitionRemoved) then + begin + FOnDefinitionRemoved(Self, Definition); + end; +end; + +destructor TInstructionEditor.Destroy; + +procedure DestroyChildFilters(Filter: TInstructionFilter); +var + I: Integer; + F: TInstructionFilter; +begin + Assert(iffIsStaticFilter in Filter.FilterFlags); + if (Filter.ItemCount > 0) then + begin + for I := 0 to Filter.Capacity - 1 do + begin + if (Assigned(Filter.Items[I])) then + begin + DestroyChildFilters(Filter.Items[I]); + F := Filter.Items[I]; + Filter.SetItem(I, nil); + F.Free; + end; + end; + end; +end; + +var + I: Integer; +begin + BeginUpdate; + try + if (Assigned(FDefinitions)) then + begin + FPreventDefinitionRemoval := true; + for I := FDefinitions.Count - 1 downto 0 do + begin + FDefinitions[I].Free; + end; + FDefinitions.Free; + end; + if Assigned(FRootTable) then + begin + DestroyChildFilters(FRootTable); + FRootTable.Free; + end; + finally + EndUpdate; + end; + inherited; +end; + +procedure TInstructionEditor.EndUpdate; +begin + if (FUpdateCount > 0) then + begin + Dec(FUpdateCount); + end; + if (FUpdateCount = 0) then + begin + if Assigned(FOnEndUpdate) then + begin + FOnEndUpdate(Self); + end; + end; +end; + +procedure TInstructionEditor.FilterChanged(Filter: TInstructionFilter); +begin + if Assigned(FOnFilterChanged) then + begin + FOnFilterChanged(Self, Filter); + end; +end; + +procedure TInstructionEditor.FilterCreated(Filter: TInstructionFilter); +begin + if Assigned(FOnFilterCreated) then + begin + FOnFilterCreated(Self, Filter); + end; +end; + +procedure TInstructionEditor.FilterDestroyed(Filter: TInstructionFilter); +begin + if Assigned(FOnFilterDestroyed) then + begin + FOnFilterDestroyed(Self, Filter); + end; +end; + +procedure TInstructionEditor.FilterInserted(Filter: TInstructionFilter); +begin + if (not Filter.IsDefinitionContainer) then Inc(FFilterCount); + if Assigned(FOnFilterInserted) then + begin + FOnFilterInserted(Self, Filter); + end; +end; + +procedure TInstructionEditor.FilterRemoved(Filter: TInstructionFilter); +begin + if (not Filter.IsDefinitionContainer) then Dec(FFilterCount); + if Assigned(FOnFilterRemoved) then + begin + FOnFilterRemoved(Self, Filter); + end; +end; + +function TInstructionEditor.GetDefinition(const Index: Integer): TInstructionDefinition; +begin + Assert((Index >= 0) and (Index < FDefinitions.Count)); + Result := FDefinitions[Index]; +end; + +function TInstructionEditor.GetDefinitionCount: Integer; +begin + Result := FDefinitions.Count; +end; + +function TInstructionEditor.GetDefinitionTopLevelFilter( + Definition: TInstructionDefinition): TInstructionFilter; +begin + Result := nil; + case Definition.Encoding of + ieDefault: + begin + case Definition.OpcodeMap of + omDefault: + Result := FRootTable; + om0F: + Result := FRootTable.Items[$0F]; + om0F38: + Result := FRootTable.Items[$0F].Items[$38]; + om0F3A: + Result := FRootTable.Items[$0F].Items[$3A]; + omXOP8, + omXOP9, + omXOPA: + Assert(false); + end; + end; + ie3DNow: + Result := FRootTable.Items[$0F].Items[$0F].Items[$01]; + ieXOP: + begin + case Definition.OpcodeMap of + omDefault, + om0F, + om0F38, + om0F3A: Assert(false); + omXOP8: + Result := FRootTable.Items[$8F].Items[$02].Items[$01]; + omXOP9: + Result := FRootTable.Items[$8F].Items[$02].Items[$02]; + omXOPA: + Result := FRootTable.Items[$8F].Items[$02].Items[$03]; + end; + end; + ieVEX: + Result := FRootTable.Items[$C4].Items[$03]; + ieEVEX: + Result := FRootTable.Items[$62].Items[$04]; + end; + Assert(Assigned(Result)); +end; + +class function TInstructionEditor.GetFilterList( + Encoding: TInstructionEncoding): PInstructionFilterList; +begin + Result := @FilterOrderDef; + case Encoding of + ieXOP: + Result := @FilterOrderXOP; + ieVEX: + Result := @FilterOrderVEX; + ieEVEX: + Result := @FilterOrderEVEX; + end; +end; + +procedure TInstructionEditor.InsertDefinition(Definition: TInstructionDefinition); +var + F, T: TInstructionFilter; + I, Index: Integer; + FilterList: PInstructionFilterList; + IsRequiredFilter: Boolean; +begin + BeginUpdate; + try + // Remove the definition from its old position + RemoveDefinition(Definition); + + // Skip all static tables. This code assumes that the parent of a static-table is always + // another static table. + // There is no need to create a static table as child of a non-static one at the moment. + F := GetDefinitionToplevelFilter(Definition); + Index := F.GetInsertPosition(Definition); + while (Assigned(F.Items[Index])) and (iffIsStaticFilter in F.Items[Index].FilterFlags) do + begin + F := F.Items[Index]; + Index := F.GetInsertPosition(Definition); + end; + + // Create required filters + FilterList := GetFilterList(Definition.Encoding); + for I := Low(FilterList^) to High(FilterList^) do + begin + // Check if the current definition requires this filter + IsRequiredFilter := false; + case FilterList^[I].GetNeutralElementType of + netNotAvailable: + IsRequiredFilter := (FilterList^[I].GetInsertPosition(Definition) >= 0); + netPlaceholder, + netValue: + IsRequiredFilter := (FilterList^[I].GetInsertPosition(Definition) > 0); + end; + + Index := F.GetInsertPosition(Definition); + + // We have to enforce this filter, if a definition in the target-slot already requires the + // same filter type + if (not IsRequiredFilter) and (FilterList^[I].GetNeutralElementType <> netNotAvailable) and + (F.Items[Index] is FilterList^[I]) then + begin + IsRequiredFilter := true; + end; + + if (IsRequiredFilter) then + begin + // If the target slot is not occupied, just go ahead and create the new filter + if (not Assigned(F.Items[Index])) then + begin + F.CreateFilterAtIndex(Index, FilterList^[I], false, false); + end; + // If the target slot is occupied by a different filter type, we need to save the old + // filter and insert it into our new one + if (F.Items[Index] is FilterList^[I]) then + begin + F := F.Items[Index]; + end else + begin + T := F.Items[Index]; + F.CreateFilterAtIndex(Index, FilterList^[I], false, false); + F := F.Items[Index]; + F.SetItem(0, T); + end; + end; + end; + + // Create a definition-container and actually insert the definition + Index := F.GetInsertPosition(Definition); + if (not Assigned(F.Items[Index])) then + begin + F.CreateFilterAtIndex(Index, TDefinitionContainer, false, false); + end; + F.Items[Index].InsertDefinition(Definition); + finally + EndUpdate; + end; +end; + +procedure TInstructionEditor.LoadFromFile(const Filename: String); +var + List: TStringList; + JSON: TJSONVariantData; +begin + List := TStringList.Create; + try + List.LoadFromFile(Filename); + JSON.Init; + if (not JSON.FromJSON(List.Text)) or (JSON.Kind <> jvObject) then + begin + raise Exception.Create('Could not parse JSON file.'); + end; + LoadFromJSON(@JSON); + finally + List.Free; + end; +end; + +procedure TInstructionEditor.LoadFromJSON(JSON: PJSONVariantData); +var + JSONDefinitions, + JSONDefinition: PJSONVariantData; + I: Integer; + Definition: TInstructionDefinition; +begin + BeginUpdate; + try + Reset; + try + if (JSON^.Kind <> jvObject) then + begin + raise Exception.Create('Invalid JSON object.'); + end; + JSONDefinitions := JSON^.Data('definitions'); + if ((not Assigned(JSONDefinitions)) or (JSONDefinitions^.Kind <> jvArray)) then + begin + raise Exception.Create( + 'The JSON object does not contain the required "definitions" array.'); + end; + if (Assigned(FOnWorkStart)) then + begin + FOnWorkStart(Self, 0, JSONDefinitions^.Count); + end; + for I := 0 to JSONDefinitions^.Count - 1 do + begin + JSONDefinition := JSONVariantDataSafe(JSONDefinitions^.Item[I], jvObject); + if (not Assigned(JSONDefinition)) then + begin + raise Exception.CreateFmt( + 'The definition item #%d is not a valid JSON object.', [I + 1]); + end; + // RegisterDefinition and InsertDefinition are indirectly called + Definition := CreateDefinition('unnamed'); + Definition.BeginUpdate; + try + try + Definition.UpdatePosition; + Definition.LoadFromJSON(JSONDefinition); + except + on E: Exception do + begin + raise Exception.CreateFmt( + 'Error while parsing definition #%d: "%s"', [I + 1, E.Message]); + end; + end; + finally + Definition.EndUpdate; + end; + if (Assigned(FOnWork)) then + begin + FOnWork(Self, I + 1); + end; + end; + if (Assigned(FOnWorkEnd)) then + begin + FOnWorkEnd(Self); + end; + except + Reset; + raise; + end; + finally + EndUpdate; + end; +end; + +procedure TInstructionEditor.RegisterDefinition(Definition: TInstructionDefinition); +begin + // This method is automatically called by TInstructionDefinition.Create + Assert(not FDefinitions.Contains(Definition)); + FDefinitions.Add(Definition); + if Assigned(FOnDefinitionCreated) then + begin + FOnDefinitionCreated(Self, Definition); + end; +end; + +procedure TInstructionEditor.RemoveDefinition(Definition: TInstructionDefinition); +var + F, P, T: TInstructionFilter; + I: Integer; + DoRemove: Boolean; +begin + if (not Assigned(Definition.Parent)) then + begin + Exit; + end; + BeginUpdate; + try + F := Definition.Parent; + F.RemoveDefinition(Definition); + if (F.DefinitionCount > 0) then + begin + Exit; + end; + // Remove empty filter tables + DoRemove := true; + while (DoRemove and Assigned(F) and (not (iffIsRootTable in F.FilterFlags))) do + begin + if (F.IsDefinitionContainer) then + begin + DoRemove := (F.DefinitionCount = 0); + end else + begin + DoRemove := (not (iffIsStaticFilter in F.FilterFlags)) and + ((F.ItemCount = 0) or ((F.NeutralElementType <> netNotAvailable) and + (F.ItemCount = 1) and (Assigned(F.Items[0])))); + end; + if (DoRemove) then + begin + Assert(Assigned(F.Parent)); + P := F.Parent; + I := P.IndexOf(F); + if (not (F.IsDefinitionContainer)) and (Assigned(F.Items[0])) then + begin + T := F.Items[0]; + F.SetItem(0, nil); + P.SetItem(I, T); + end else + begin + P.SetItem(I, nil); + end; + F.Free; + F := P; + end; + end; + finally + EndUpdate; + end; +end; + +procedure TInstructionEditor.Reset; +var + I: Integer; +begin + BeginUpdate; + try + FPreventDefinitionRemoval := true; + for I := FDefinitions.Count - 1 downto 0 do + begin + FDefinitions[I].Free; + end; + FPreventDefinitionRemoval := false; + FDefinitions.Clear; + if (not Assigned(FRootTable)) then + begin + FFilterCount := 1; + // 1, 2 and 3 Byte Opcode Tables + FRootTable := TOpcodeFilter.Create(Self, nil, true, true); + FRootTable.CreateFilterAtIndex($0F, TOpcodeFilter, false, true); + FRootTable.Items[$0F].CreateFilterAtIndex($38, TOpcodeFilter, false, true); + FRootTable.Items[$0F].CreateFilterAtIndex($3A, TOpcodeFilter, false, true); + // 3DNow Table + FRootTable.Items[$0F].CreateFilterAtIndex($0F, TEncodingFilter, false, true); + FRootTable.Items[$0F].Items[$0F].CreateFilterAtIndex($01, TOpcodeFilter, false, true); + // 3 Byte VEX Table + FRootTable.CreateFilterAtIndex($C4, TEncodingFilter, false, true); + FRootTable.Items[$C4].CreateFilterAtIndex($03, TVEXMapFilter, false, true); + // 2 Byte VEX Table (we copy the 3 byte VEX table later) + FRootTable.CreateFilterAtIndex($C5, TEncodingFilter, false, true); + FRootTable.Items[$C5].CreateFilterAtIndex($03, TVEXMapFilter, false, true); + // XOP Table + FRootTable.CreateFilterAtIndex($8F, TEncodingFilter, false, true); + FRootTable.Items[$8F].CreateFilterAtIndex($02, TXOPMapFilter, false, true); + for I := 1 to FRootTable.Items[$8F].Items[$02].Capacity - 1 do + begin + FRootTable.Items[$8F].Items[$02].CreateFilterAtIndex(I, TOpcodeFilter, false, true); + end; + // EVEX Table + FRootTable.CreateFilterAtIndex($62, TEncodingFilter, false, true); + FRootTable.Items[$62].CreateFilterAtIndex($04, TVEXMapFilter, false, true); + end; + finally + EndUpdate; + end; +end; + +procedure TInstructionEditor.SaveToFile(const Filename: String); +var + JSON: TJSONVariantData; + List: TStringList; +begin + JSON.Init; + SaveToJSON(@JSON); + List := TStringList.Create; + try + List.Text := TJSONHelper.JSONToString(@JSON); + List.SaveToFile(FileName); + finally + List.Free; + end; +end; + +procedure TInstructionEditor.SaveToJSON(JSON: PJSONVariantData); +var + Comparison: TComparison; + I: Integer; + JSONDefinitionList, JSONDefinition: TJSONVariantData; +begin + // Sort definitions by mnemonic + Comparison := + function(const Left, Right: TInstructionDefinition): Integer + begin + Result := CompareStr(Left.Mnemonic, Right.Mnemonic); + end; + FDefinitions.Sort(TComparer.Construct(Comparison)); + // Save to JSON + if (Assigned(FOnWorkStart)) then + begin + FOnWorkStart(Self, 0, FDefinitions.Count); + end; + JSONDefinitionList.Init; + for I := 0 to FDefinitions.Count - 1 do + begin + JSONDefinition.Init; + FDefinitions[I].SaveToJSON(@JSONDefinition); + JSONDefinitionList.AddValue(Variant(JSONDefinition)); + if (Assigned(FOnWork)) then + begin + FOnWork(Self, I + 1); + end; + end; + JSON^.AddNameValue('definitions', Variant(JSONDefinitionList)); + if (Assigned(FOnWorkEnd)) then + begin + FOnWorkEnd(Self); + end; +end; + +procedure TInstructionEditor.UnregisterDefinition(Definition: TInstructionDefinition); +begin + // This method is automatically called by TInstructionDefinition.Destroy + Assert(FDefinitions.Contains(Definition)); + if Assigned(FOnDefinitionDestroyed) then + begin + FOnDefinitionDestroyed(Self, Definition); + end; + if (not FPreventDefinitionRemoval) then + begin + FDefinitions.Remove(Definition); + end; +end; +{$ENDREGION} + +{$REGION 'Const: Constants used by TTableGenerator'} +const + MNEMONIC_ALIASES: array[0..0] of String = ( + 'nop' + ); + FILENAME_INSTRUCTIONTABLE = 'InstructionTable.inc'; + FILENAME_MNEMONICENUM = 'Mnemonics.inc'; + FILENAME_MNEMONICSTRINGS = 'MnemonicStrings.inc'; + FILENAME_INSTRUCTIONDEFINITIONS = 'InstructionDefinitions.inc'; + SIZEOF_INSTRUCTIONTABLENODE = 3; + SIZEOF_INSTRUCTIONDEFINITION = 10; + TYPEOF_INSTRUCTIONTABLENODE = 'ZydisInstructionTableNode'; + TYPEOF_INSTRUCTIONOPERANDS = 'ZydisInstructionOperands'; + TYPEOF_INSTRUCTIONDEFINITION = 'ZydisInstructionDefinition'; + INSTRUCTIONTABLENODE_INVALID = 'ZYDIS_INVALID'; + INSTRUCTIONTABLENODE_FILTER = 'ZYDIS_FILTER'; + INSTRUCTIONTABLENODE_DEFINITION = 'ZYDIS_DEFINITION'; + PREFIX_FILTERARRAY = 'filter'; + PREFIX_FILTERTYPE = 'ZYDIS_NODETYPE_FILTER_'; + PREFIX_MNEMONIC = 'ZYDIS_MNEMONIC_'; + INSTRUCTIONDEFINITION_DEFINITION = 'ZYDIS_MAKE_DEFINITION'; + INSTRUCTIONDEFINITION_OPERAND = 'ZYDIS_MAKE_OPERAND'; + PREFIX_OPERAND_TYPE = 'ZYDIS_SEM_OPERAND_TYPE_'; + PREFIX_OPERAND_ENCODING = 'ZYDIS_OPERAND_ENCODING_'; + PREFIX_OPERAND_ACCESSMODE = 'ZYDIS_OPERAND_ACCESS_'; + ARRAYNAME_INSTRUCTIONOPERANDS = 'instructionOperands'; + ARRAYNAME_INSTRUCTIONDEFINITIONS = 'instructionDefinitions'; +{$ENDREGION} + +{$REGION 'Class: TTableGenerator'} +constructor TTableGenerator.Create; +begin + inherited Create; + +end; + +procedure TTableGenerator.CreateEntityLists(Editor: TInstructionEditor; + var FilterList: TIndexedFilterList; var DefinitionList: TIndexedDefinitionList; + var MnemonicList: TMnemonicList); + +var + IndexDict: TDictionary; + +procedure CreateChildIndizes(var Root: TIndexedFilterItem); +var + I, J: Integer; +begin + SetLength(Root.Items, Root.Filter.Capacity); + FillChar(Root.Items[0], Length(Root.Items) * SizeOf(Root.Items[0]), #0); + for I := 0 to Root.Filter.Capacity - 1 do + begin + Root.Items[I].Id := -1; + Root.Items[I].Filter := Root.Filter.Items[I]; + if (Assigned(Root.Items[I].Filter)) then + begin + if (not IndexDict.ContainsKey(TInstructionFilterClass(Root.Filter.Items[I].ClassType))) then + begin + Root.Items[I].Id := 0; + IndexDict.Add(TInstructionFilterClass(Root.Filter.Items[I].ClassType), 1); + end else + begin + Root.Items[I].Id := IndexDict[TInstructionFilterClass(Root.Filter.Items[I].ClassType)]; + IndexDict[TInstructionFilterClass(Root.Filter.Items[I].ClassType)] := Root.Items[I].Id + 1; + end; + + if (Root.Items[I].Filter.IsDefinitionContainer) then + begin + // Fix mnemonic index + for J := Low(DefinitionList) to High(DefinitionList) do + begin + if (DefinitionList[J].Definition.Parent = Root.Items[I].Filter) then + begin + Root.Items[I].Id := DefinitionList[J].Id; + Break; + end; + end; + end else + begin + Inc(FStatistics.FilterCount); + Inc(FStatistics.FilterSize, Root.Items[I].Filter.GetCapacity * SIZEOF_INSTRUCTIONTABLENODE); + if (Root.Items[I].Filter.NeutralElementType = netPlaceholder) then + begin + Dec(FStatistics.FilterSize, SIZEOF_INSTRUCTIONTABLENODE); + end; + Work(FStatistics.FilterCount); + end; + + CreateChildIndizes(Root.Items[I]); + end; + end; +end; + +var + ListDict: TDictionary>; + +procedure AddFiltersToListDict(const Root: TIndexedFilterItem); +var + FilterList: TList; + I: Integer; +begin + if (Root.IsRedirect) then Exit; + if (not ListDict.ContainsKey(TInstructionFilterClass(Root.Filter.ClassType))) then + begin + FilterList := TList.Create; + ListDict.Add(TInstructionFilterClass(Root.Filter.ClassType), FilterList); + end else + begin + FilterList := ListDict[TInstructionFilterClass(Root.Filter.ClassType)]; + end; + FilterList.Add(Root); + for I := Low(Root.Items) to High(Root.Items) do + begin + if (Root.Items[I].Id < 0) or (Root.Items[I].Filter is TEncodingFilter) then Continue; + AddFiltersToListDict(Root.Items[I]); + end; +end; + +var + DList: TList; + DComparison: TComparison; + MList: TList; + MComparison: TComparison; + I, J, K: Integer; + Root, Temp: TIndexedFilterItem; + A: TArray>>; +begin + // Create definition indizes and a sorted definition-list + DList := TList.Create; + try + WorkStart(woIndexingDefinitions, 0, Editor.DefinitionCount * 2); + for I := 0 to Editor.DefinitionCount - 1 do + begin + DList.Add(Editor.Definitions[I]); + Work(I + 1); + end; + DComparison := + function(const Left, Right: TInstructionDefinition): Integer + begin + Result := CompareStr(Left.Mnemonic, Right.Mnemonic); + end; + DList.Sort(TComparer.Construct(DComparison)); + SetLength(DefinitionList, DList.Count); + for I := 0 to DList.Count - 1 do + begin + DefinitionList[I].Id := I; + DefinitionList[I].Definition := DList[I]; + Work(Editor.DefinitionCount + I + 1); + end; + WorkEnd; + finally + DList.Free; + end; + FStatistics.DefinitionCount := Length(DefinitionList); + FStatistics.DefinitionSize := Length(DefinitionList) * SIZEOF_INSTRUCTIONDEFINITION; + + // Create sorted mnemonic list with all aliases + FStatistics.MnemonicSize := 0; + MList := TList.Create; + try + for I := 0 to Editor.DefinitionCount - 1 do + begin + MList.Add(Editor.Definitions[I].Mnemonic); + end; + for I := Low(MNEMONIC_ALIASES) to High(MNEMONIC_ALIASES) do + begin + MList.Add(MNEMONIC_ALIASES[I]); + end; + MComparison := + function(const Left, Right: String): Integer + begin + Result := CompareStr(Left, Right); + end; + MList.Sort(TComparer.Construct(MComparison)); + for I := MList.Count - 1 downto 1 do + begin + if (MList[I] = MList[I - 1]) then + begin + MList.Delete(I); + end; + end; + MList.Insert(0, 'invalid'); + SetLength(MnemonicList, MList.Count); + for I := 0 to MList.Count - 1 do + begin + MnemonicList[I] := MList[I]; + Inc(FStatistics.MnemonicSize, Length(MnemonicList[I])); + end; + finally + MList.Free; + end; + FStatistics.MnemonicCount := Length(MnemonicList); + + IndexDict := TDictionary.Create; + try + // Generate internal tree structure + Root.Id := 0; + Root.Filter := Editor.RootTable; + Root.IsRedirect := false; + IndexDict.Add(TOpcodeFilter, 1); + FStatistics.FilterCount := 1; + FStatistics.FilterSize := 256 * SIZEOF_INSTRUCTIONTABLENODE; + WorkStart(woIndexingFilters, 0, Editor.FilterCount - 1); + CreateChildIndizes(Root); + WorkEnd; + + // Unlink encoding filters + Root.Items[$0F].Items[$0F] := Root.Items[$0F].Items[$0F].Items[$01]; + Temp := Root.Items[$C4].Items[$03]; + Temp.Items[$00] := Root.Items[$C4].Items[$00]; + Root.Items[$C4] := Temp; + Temp := Root.Items[$C5].Items[$03]; + Temp.Items[$00] := Root.Items[$C5].Items[$00]; + Root.Items[$C5] := Temp; + Temp := Root.Items[$62].Items[$04]; + Temp.Items[$00] := Root.Items[$62].Items[$00]; + Root.Items[$62] := Temp; + Temp := Root.Items[$8F].Items[$02]; + Temp.Items[$00] := Root.Items[$8F].Items[$00]; + Root.Items[$8F] := Temp; + + // Initialize 2-byte VEX filter + Root.Items[$C5].Items[$01] := Root.Items[$C4].Items[$01]; // 0x0F + Root.Items[$C5].Items[$01].IsRedirect := true; + Root.Items[$C5].Items[$05] := Root.Items[$C4].Items[$05]; // 0x66 0x0F + Root.Items[$C5].Items[$05].IsRedirect := true; + Root.Items[$C5].Items[$09] := Root.Items[$C4].Items[$09]; // 0xF3 0x0F + Root.Items[$C5].Items[$09].IsRedirect := true; + Root.Items[$C5].Items[$0D] := Root.Items[$C4].Items[$0D]; // 0xF2 0x0F + Root.Items[$C5].Items[$0D].IsRedirect := true; + + Dec(FStatistics.FilterCount, 5); + Dec(FStatistics.FilterSize, 5 * 5 * SIZEOF_INSTRUCTIONTABLENODE); + + // Generate filter list + ListDict := + TObjectDictionary>.Create([doOwnsValues]); + try + AddFiltersToListDict(Root); + A := ListDict.ToArray; + SetLength(FilterList, Length(A)); + for I := Low(A) to High(A) do + begin + FilterList[I].Key := A[I].Key; + FilterList[I].Value := A[I].Value.ToArray; + // Clear recursive child-item arrays + for J := Low(FilterList[I].Value) to HigH(FilterList[I].Value) do + begin + for K := Low(FilterList[I].Value[J].Items) to High(FilterList[I].Value[J].Items) do + begin + SetLength(FilterList[I].Value[J].Items[K].Items, 0); + end; + end; + end; + finally + ListDict.Free; + end; + finally + IndexDict.Free; + end; +end; + +destructor TTableGenerator.Destroy; +begin + + inherited; +end; + +procedure TTableGenerator.GenerateDefinitionList(const OutputDirectory: String; + const DefinitionList: PIndexedDefinitionList); + +procedure AppendOperand(Buffer: TStringBuffer; Operand: TInstructionOperand); +var + OperandType, + OperandEncoding, + OperandAccessMode: String; +begin + OperandType := 'UNUSED'; + case Operand.OperandType of + optGPR8 : OperandType := 'GPR8'; + optGPR16 : OperandType := 'GPR16'; + optGPR32 : OperandType := 'GPR32'; + optGPR64 : OperandType := 'GPR64'; + optFPR : OperandType := 'FPR'; + optVR64 : OperandType := 'VR64'; + optVR128 : OperandType := 'VR128'; + optVR256 : OperandType := 'VR256'; + optVR512 : OperandType := 'VR512'; + optCR : OperandType := 'CR'; + optDR : OperandType := 'DR'; + optMSKR : OperandType := 'MSKR'; + optBNDR : OperandType := 'BNDR'; + optMem : OperandType := 'MEM'; + optMem8 : OperandType := 'MEM8'; + optMem16 : OperandType := 'MEM16'; + optMem32 : OperandType := 'MEM32'; + optMem64 : OperandType := 'MEM64'; + optMem80 : OperandType := 'MEM80'; + optMem128 : OperandType := 'MEM128'; + optMem256 : OperandType := 'MEM256'; + optMem512 : OperandType := 'MEM512'; + optMem32Bcst2 : OperandType := 'MEM32_BCST2'; + optMem32Bcst4 : OperandType := 'MEM32_BCST4'; + optMem32Bcst8 : OperandType := 'MEM32_BCST8'; + optMem32Bcst16: OperandType := 'MEM32_BCST16'; + optMem64Bcst2 : OperandType := 'MEM64_BCST2'; + optMem64Bcst4 : OperandType := 'MEM64_BCST4'; + optMem64Bcst8 : OperandType := 'MEM64_BCST8'; + optMem64Bcst16: OperandType := 'MEM64_BCST16'; + optMem112 : OperandType := 'MEM112'; + optMem224 : OperandType := 'MEM224'; + optImm8 : OperandType := 'IMM8'; + optImm16 : OperandType := 'IMM16'; + optImm32 : OperandType := 'IMM32'; + optImm64 : OperandType := 'IMM64'; + optImm8U : OperandType := 'IMM8U'; + optRel8 : OperandType := 'REL8'; + optRel16 : OperandType := 'REL16'; + optRel32 : OperandType := 'REL32'; + optRel64 : OperandType := 'REL64'; + optPtr1616 : OperandType := 'PTR1616'; + optPtr1632 : OperandType := 'PTR1632'; + optPtr1664 : OperandType := 'PTR1664'; + optMoffs16 : OperandType := 'MOFFS16'; + optMoffs32 : OperandType := 'MOFFS32'; + optMoffs64 : OperandType := 'MOFFS64'; + optSrcIndex8 : OperandType := 'SRCIDX8'; + optSrcIndex16 : OperandType := 'SRCIDX16'; + optSrcIndex32 : OperandType := 'SRCIDX32'; + optSrcIndex64 : OperandType := 'SRCIDX64'; + optDstIndex8 : OperandType := 'DSTIDX8'; + optDstIndex16 : OperandType := 'DSTIDX16'; + optDstIndex32 : OperandType := 'DSTIDX32'; + optDstIndex64 : OperandType := 'DSTIDX64'; + optSREG : OperandType := 'SREG'; + optMem1616 : OperandType := 'M1616'; + optMem1632 : OperandType := 'M1632'; + optMem1664 : OperandType := 'M1664'; + optMem32VSIBX : OperandType := 'MEM32_VSIBX'; + optMem32VSIBY : OperandType := 'MEM32_VSIBY'; + optMem32VSIBZ : OperandType := 'MEM32_VSIBZ'; + optMem64VSIBX : OperandType := 'MEM64_VSIBX'; + optMem64VSIBY : OperandType := 'MEM64_VSIBY'; + optMem64VSIBZ : OperandType := 'MEM64_VSIBZ'; + optFixed1 : OperandType := 'FIXED1'; + optFixedAL : OperandType := 'AL'; + optFixedCL : OperandType := 'CL'; + optFixedAX : OperandType := 'AX'; + optFixedDX : OperandType := 'DX'; + optFixedEAX : OperandType := 'EAX'; + optFixedRAX : OperandType := 'RAX'; + optFixedES : OperandType := 'ES'; + optFixedCS : OperandType := 'CS'; + optFixedSS : OperandType := 'SS'; + optFixedDS : OperandType := 'DS'; + optFixedGS : OperandType := 'GS'; + optFixedFS : OperandType := 'FS'; + optFixedST0 : OperandType := 'ST0'; + end; + OperandEncoding := 'NONE'; + case Operand.Encoding of + opeModrmReg : OperandEncoding := 'REG'; + opeModrmRm : OperandEncoding := 'RM'; + opeModrmRmCD1 : OperandEncoding := 'RM'; + opeModrmRmCD2 : OperandEncoding := 'RM_CD2'; + opeModrmRmCD4 : OperandEncoding := 'RM_CD4'; + opeModrmRmCD8 : OperandEncoding := 'RM_CD8'; + opeModrmRmCD16: OperandEncoding := 'RM_CD16'; + opeModrmRmCD32: OperandEncoding := 'RM_CD32'; + opeModrmRmCD64: OperandEncoding := 'RM_CD64'; + opeOpcodeBits : OperandEncoding := 'OPCODE'; + opeVexVVVV : OperandEncoding := 'VVVV'; + opeEvexAAA : OperandEncoding := 'AAA'; + opeImm8 : OperandEncoding := 'IMM8'; + opeImm16 : OperandEncoding := 'IMM16'; + opeImm32 : OperandEncoding := 'IMM32'; + opeImm64 : OperandEncoding := 'IMM64'; + end; + OperandAccessMode := 'READ'; + case Operand.AccessMode of + opaWrite : OperandAccessMode := 'WRITE'; + opaReadWrite : OperandAccessMode := 'READWRITE'; + end; + Buffer.Append(Format('%s(%s%s, %s%s, %s%s)', [INSTRUCTIONDEFINITION_OPERAND, + PREFIX_OPERAND_TYPE, OperandType, PREFIX_OPERAND_ENCODING, OperandEncoding, + PREFIX_OPERAND_ACCESSMODE, OperandAccessMode])); +end; + +var + Buffer: TStringBuffer; + StringList: TStringList; + I, J: Integer; + + S, T, U: String; + + Operands: TList; + B: Boolean; +begin + Operands := TList.Create; + try + for I := Low(DefinitionList^) to High(DefinitionList^) do + begin + B := false; + for J := 0 to Operands.Count - 1 do + begin + if (Operands[J].Equals(DefinitionList^[I].Definition.Operands)) then + begin + B := true; + Break; + end; + end; + if (not B) then + begin + Operands.Add(DefinitionList^[I].Definition.Operands); + end; + end; + + Buffer := TStringBuffer.Create; + try + Buffer.AppendLn(Format('const %s %s[] =', [ + TYPEOF_INSTRUCTIONOPERANDS, ARRAYNAME_INSTRUCTIONOPERANDS])); + Buffer.AppendLn('{'); + for I := 0 to Operands.Count - 1 do + begin + Buffer.Append(Format(' /*%.4x*/ { ', [I])); + for J := 0 to 3 do + begin + AppendOperand(Buffer, Operands[I].GetOperandById(J)); + if (J <> 3) then + begin + Buffer.Append(', '); + end; + end; + if (I <> (Operands.Count - 1)) then + begin + Buffer.AppendLn(' },'); + end else + begin + Buffer.AppendLn(' }'); + end; + end; + Buffer.AppendLn('};'); + Buffer.AppendLn(''); + + Buffer.AppendLn(Format('const %s %s[] =', [ + TYPEOF_INSTRUCTIONDEFINITION, ARRAYNAME_INSTRUCTIONDEFINITIONS])); + Buffer.AppendLn('{'); + WorkStart(woGeneratingDefinitionFiles, 0, Length(DefinitionList^)); + for I := Low(DefinitionList^) to High(DefinitionList^) do + begin + Buffer.Append(Format(' /*%.4x*/ ', [I])); + //Buffer.Append(Format('{ %s%s, { ', [ + Buffer.Append(Format('ZYDIS_MAKE_DEFINITION(%s%s, ', [ + PREFIX_MNEMONIC, AnsiUpperCase(DefinitionList^[I].Definition.Mnemonic)])); + {for J := 0 to 3 do + begin + AppendOperand(Buffer, DefinitionList^[I].Definition.Operands.GetOperandById(J)); + if (J <> 3) then + begin + Buffer.Append(', '); + end; + end;} + + for J := 0 to Operands.Count - 1 do + begin + if (Operands[J].Equals(DefinitionList^[I].Definition.Operands)) then + begin + Buffer.Append(Format('0x%.4x', [J])); + Break; + end; + end; + + //Buffer.Append(' }'); + + S := '0'; T := '0'; U := '0'; + if (ifAcceptsEvexAAA in DefinitionList^[I].Definition.Flags) then S := '1'; + if (ifAcceptsEvexZ in DefinitionList^[I].Definition.Flags) then T := '1'; + if (ifHasEvexBC in DefinitionList^[I].Definition.Flags) then U := '1' + else + if (ifHasEvexRC in DefinitionList^[I].Definition.Flags) then U := '2' + else + if (ifHasEvexSAE in DefinitionList^[I].Definition.Flags) then U := '3'; + + Buffer.Append(Format(', ZYDIS_MAKE_AVX512INFO(%s, %s, %s)', [U, S, T])); + + if (I <> High(DefinitionList^)) then + begin + //Buffer.AppendLn(' },'); + Buffer.AppendLn(' ),'); + end else + begin + //Buffer.AppendLn(' }'); + Buffer.AppendLn(' )'); + end; + Work(I + 1); + end; + WorkEnd; + if (Length(DefinitionList^) = 0) then + begin + Buffer.AppendLn(Format(' /*0000*/ { %sINVALID }', [PREFIX_MNEMONIC])); + end; + Buffer.AppendLn('};'); + StringList := TStringList.Create; + try + StringList.Text := Buffer.Value; + StringList.SaveToFile(OutputDirectory + FILENAME_INSTRUCTIONDEFINITIONS); + finally + StringList.Free; + end; + finally + Buffer.Free; + end; + + finally + Operands.Free; + end; +end; + +procedure TTableGenerator.GenerateFiles(Editor: TInstructionEditor; const OutputDirectory: String); +var + FilterList: TIndexedFilterList; + DefinitionList: TIndexedDefinitionList; + MnemonicList: TMnemonicList; +begin + // Check error cases + if (not Assigned(Editor.RootTable)) then + begin + raise Exception.Create('The instruction editor does not contain tables.'); + end; + if (Editor.RootTable.HasConflicts) then + begin + raise Exception.Create('The instruction editor has unresolved conflicts.'); + end; + + CreateEntityLists(Editor, FilterList, DefinitionList, MnemonicList); + GenerateInstructionTable(OutputDirectory, @FilterList, FStatistics.FilterCount); + GenerateDefinitionList(OutputDirectory, @DefinitionList); + GenerateMnemonicLists(OutputDirectory, @MnemonicList); +end; + +procedure TTableGenerator.GenerateInstructionTable(const OutputDirectory: String; + const FilterList: PIndexedFilterList; FilterCount: Integer); +var + Buffer: TStringBuffer; + StringList: TStringList; + A: ^TArray; + WorkCount, + IndexShift: Integer; + I, J, K: Integer; +begin + Buffer := TStringBuffer.Create; + try + WorkCount := 0; + WorkStart(woGeneratingFilterFiles, 0, FilterCount); + for I := Low(InstructionFilterClasses) to High(InstructionFilterClasses) do + begin + if (InstructionFilterClasses[I] = TEncodingFilter) then Continue; + IndexShift := 0; + if (InstructionFilterClasses[I].GetNeutralElementType = netPlaceholder) then + begin + IndexShift := 1; + end; + + // Open the filter-array + Buffer.AppendLn(Format('const %s %s%s[][%d] = ', [ + TYPEOF_INSTRUCTIONTABLENODE, PREFIX_FILTERARRAY, + InstructionFilterClasses[I].GetDescription, + Integer(InstructionFilterClasses[I].GetCapacity) - IndexShift])); + Buffer.AppendLn('{'); + + A := nil; + for J := Low(FilterList^) to High(FilterList^) do + begin + if (FilterList^[J].Key = InstructionFilterClasses[I]) then + begin + A := @FilterList^[J].Value; + Break; + end; + end; + + if (Assigned(A)) then + begin + // Add all filters of the current type + for J := Low(A^) to High(A^) do + begin + + // Open the local filter array + Buffer.AppendLn(' {'); + + // Add all filter values of the current filter + for K := IndexShift to High(A^[J].Items) do + begin + Buffer.Append(Format(' /*%.4x*/ ', [K])); + if (A^[J].Items[K].Id < 0) then + begin + Buffer.Append(INSTRUCTIONTABLENODE_INVALID); + end else if (A^[J].Items[K].Filter is TDefinitionContainer) then + begin + Assert((A^[J].Items[K].Filter as TDefinitionContainer).DefinitionCount = 1); + Buffer.Append(Format('%s(0x%.4x)', [ + INSTRUCTIONTABLENODE_DEFINITION, A^[J].Items[K].Id])); + end else + begin + Buffer.Append(Format('%s(%s%s, 0x%.4x)', [ + INSTRUCTIONTABLENODE_FILTER, PREFIX_FILTERTYPE, AnsiUpperCase( + TInstructionFilterClass(A^[J].Items[K].Filter.ClassType).GetDescription), + A^[J].Items[K].Id])); + end; + if (K < High(A^[J].Items)) then + begin + Buffer.AppendLn(','); + end else + begin + Buffer.AppendLn(''); + end; + end; + + // Close the local filter array + Buffer.Append(' }'); + if (J < High(A^)) then + begin + Buffer.AppendLn(','); + end else + begin + Buffer.AppendLn(''); + end; + + Inc(WorkCount); + Work(WorkCount); + end; + end else + begin + Buffer.AppendLn(' {'); + for J := IndexShift to InstructionFilterClasses[I].GetCapacity - 1 do + begin + Buffer.Append(Format(' /*%.4x*/ %s', [J, INSTRUCTIONTABLENODE_INVALID])); + if (J < Integer(InstructionFilterClasses[I].GetCapacity - 1)) then + begin + Buffer.AppendLn(','); + end else + begin + Buffer.AppendLn(''); + end; + end; + Buffer.AppendLn(' }'); + end; + + // Close the filter array + Buffer.AppendLn('};'); + if (I < High(InstructionFilterClasses)) then + begin + Buffer.AppendLn(''); + end; + + end; + WorkEnd; + + StringList := TStringList.Create; + try + StringList.Text := Buffer.Value; + StringList.SaveToFile(OutputDirectory + FILENAME_INSTRUCTIONTABLE); + finally + StringList.Free; + end; + finally + Buffer.Free; + end; +end; + +procedure TTableGenerator.GenerateMnemonicLists(const OutputDirectory: String; + const MnemonicList: PMnemonicList); +var + I: Integer; + MnemonicEnum, + MnemonicStrings: TStringBuffer; + StringList: TStringList; +begin + MnemonicEnum := TStringBuffer.Create; + try + MnemonicStrings := TStringBuffer.Create; + try + WorkStart(woGeneratingMnemonicFiles, 0, Length(MnemonicList^)); + for I := Low(MnemonicList^) to High(MnemonicList^) do + begin + MnemonicEnum.Append(Format(' /*%.4x*/ %s%s', [ + I, PREFIX_MNEMONIC, AnsiUpperCase(MnemonicList^[I])])); + MnemonicStrings.Append(Format(' /*%.4x*/ "%s"', [I, MnemonicList^[I]])); + if (I <> High(MnemonicList^)) then + begin + MnemonicEnum.AppendLn(','); + MnemonicStrings.AppendLn(','); + end else + begin + MnemonicEnum.AppendLn(''); + MnemonicStrings.AppendLn(''); + end; + Work(I + 1); + end; + WorkEnd; + StringList := TStringList.Create; + try + StringList.Text := MnemonicEnum.Value; + StringList.SaveToFile(OutputDirectory + FILENAME_MNEMONICENUM); + StringList.Text := MnemonicStrings.Value; + StringList.SaveToFile(OutputDirectory + FILENAME_MNEMONICSTRINGS); + finally + StringList.Free; + end; + finally + MnemonicStrings.Free; + end; + finally + MnemonicEnum.Free; + end; +end; + +procedure TTableGenerator.Work(WorkCount: Integer); +begin + if (Assigned(FOnWork)) then + begin + FOnWork(Self, WorkCount); + end; +end; + +procedure TTableGenerator.WorkEnd; +begin + if (Assigned(FOnWorkEnd)) then + begin + FOnWorkEnd(Self); + end; +end; + +procedure TTableGenerator.WorkStart(Operation: TGeneratorWorkOperation; MinWorkCount, + MaxWorkCount: Integer); +begin + if (Assigned(FOnWorkStart)) then + begin + FOnWorkStart(Self, Operation, MinWorkCount, MaxWorkCount); + end; +end; +{$ENDREGION} + +end. + diff --git a/assets/InstructionEditor/untInstructionFilters.pas b/assets/InstructionEditor/untInstructionFilters.pas new file mode 100644 index 0000000..e547ebd --- /dev/null +++ b/assets/InstructionEditor/untInstructionFilters.pas @@ -0,0 +1,607 @@ +unit untInstructionFilters; + +interface + +uses + untInstructionEditor; + +type + TOpcodeFilter = class(TInstructionFilter) + public + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetDescription: String; override; + end; + + TEncodingFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TVEXMapFilter = class(TInstructionFilter) + public + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TXOPMapFilter = class(TInstructionFilter) + public + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TModeFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TMandatoryPrefixFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TModrmModFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TModrmRegFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TModrmRmFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TOperandSizeFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TAddressSizeFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TRexWFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TVexLFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TEvexL2Filter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + + TEvexBFilter = class(TInstructionFilter) + public + class function GetNeutralElementType: TNeutralElementType; override; + class function GetCapacity: Cardinal; override; + class function GetInsertPosition(const Definition: TInstructionDefinition): Integer; override; + class function GetItemDescription(Index: Integer): String; override; + class function GetDescription: String; override; + end; + +const + InstructionFilterClasses: array[0..14] of TInstructionFilterClass = + ( + TOpcodeFilter, + TEncodingFilter, + TVEXMapFilter, + TXOPMapFilter, + TModeFilter, + TMandatoryPrefixFilter, + TModrmModFilter, + TModrmRegFilter, + TModrmRmFilter, + TOperandSizeFilter, + TAddressSizeFilter, + TRexWFilter, + TVexLFilter, + TEvexL2Filter, + TEvexBFilter + ); + +implementation + +uses + System.SysUtils, System.TypInfo; + +{$REGION 'Class: TOpcodeFilter'} +class function TOpcodeFilter.GetCapacity: Cardinal; +begin + Result := 256; +end; + +class function TOpcodeFilter.GetDescription: String; +begin + Result := 'Opcode'; +end; + +class function TOpcodeFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Result := Definition.Opcode; +end; +{$ENDREGION} + +{$REGION 'Class: TEncodingFilter'} +class function TEncodingFilter.GetCapacity: Cardinal; +begin + Result := 5; +end; + +class function TEncodingFilter.GetDescription: String; +begin + Result := 'Encoding'; +end; + +class function TEncodingFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.Encoding); +end; + +class function TEncodingFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TInstructionEncoding), Index); +end; + +class function TEncodingFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netValue; +end; +{$ENDREGION} + +{$REGION 'Class: TVEXMapFilter'} +class function TVEXMapFilter.GetCapacity: Cardinal; +begin + Result := 16; +end; + +class function TVEXMapFilter.GetDescription: String; +begin + Result := 'VEX'; +end; + +class function TVEXMapFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Assert(Definition.OpcodeMap < omXOP8); + Result := (Integer(Definition.OpcodeExtensions.MandatoryPrefix)) * 4 + Byte(Definition.OpcodeMap); +end; + +class function TVEXMapFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + case (Index div 4) of + 0: Result := '##'; + 1: Result := '66'; + 2: Result := 'F3'; + 3: Result := 'F2'; + end; + case (Index mod 4) of + 0: Result := Result + ' default'; + 1: Result := Result + ' 0F'; + 2: Result := Result + ' 0F 38'; + 3: Result := Result + ' 0F 3A'; + end; +end; +{$ENDREGION} + +{$REGION 'Class: TXOPMapFilter'} +class function TXOPMapFilter.GetCapacity: Cardinal; +begin + Result := 4; +end; + +class function TXOPMapFilter.GetDescription: String; +begin + Result := 'XOP'; +end; + +class function TXOPMapFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Assert(Definition.Encoding = ieXOP); + Assert(Definition.OpcodeMap >= omXOP8); + Result := Integer(Definition.OpcodeMap) - Integer(omXOP8) + 1; +end; + +class function TXOPMapFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + if (Index = 0) then + begin + Result := 'Non-XOP-Placeholder'; + end else + begin + Result := GetEnumName(TypeInfo(TOpcodeMap), Integer(omXOP8) + Index - 1); + end; +end; +{$ENDREGION} + +{$REGION 'Class: TModeFilter'} +class function TModeFilter.GetCapacity: Cardinal; +begin + Result := 3; +end; + +class function TModeFilter.GetDescription: String; +begin + Result := 'Mode'; +end; + +class function TModeFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.Mode); +end; + +class function TModeFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtInstructionMode), Index); +end; + +class function TModeFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netPlaceholder; +end; +{$ENDREGION} + +{$REGION 'Class: TMandatoryPrefixFilter'} +class function TMandatoryPrefixFilter.GetCapacity: Cardinal; +begin + Result := 4; +end; + +class function TMandatoryPrefixFilter.GetDescription: String; +begin + Result := 'MandatoryPrefix'; +end; + +class function TMandatoryPrefixFilter.GetInsertPosition( + const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.MandatoryPrefix); +end; + +class function TMandatoryPrefixFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtMandatoryPrefix), Index); +end; + +class function TMandatoryPrefixFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netValue; +end; +{$ENDREGION} + +{$REGION 'Class: TModrmModFilter'} +class function TModrmModFilter.GetCapacity: Cardinal; +begin + Result := 3; +end; + +class function TModrmModFilter.GetDescription: String; +begin + Result := 'ModrmMod'; +end; + +class function TModrmModFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.ModrmMod); +end; + +class function TModrmModFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtModrmMod), Index); +end; + +class function TModrmModFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netPlaceholder; +end; +{$ENDREGION} + +{$REGION 'Class: TModrmRegFilter'} +class function TModrmRegFilter.GetCapacity: Cardinal; +begin + Result := 9; +end; + +class function TModrmRegFilter.GetDescription: String; +begin + Result := 'ModrmReg'; +end; + +class function TModrmRegFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.ModrmReg); +end; + +class function TModrmRegFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtModrmReg), Index); +end; + +class function TModrmRegFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netPlaceholder; +end; +{$ENDREGION} + +{$REGION 'Class: TModrmRmFilter'} +class function TModrmRmFilter.GetCapacity: Cardinal; +begin + Result := 9; +end; + +class function TModrmRmFilter.GetDescription: String; +begin + Result := 'ModrmRm'; +end; + +class function TModrmRmFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.ModrmRm); +end; + +class function TModrmRmFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtModrmRm), Index); +end; + +class function TModrmRmFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netPlaceholder; +end; +{$ENDREGION} + +{$REGION 'Class: TOperandSizeFilter'} +class function TOperandSizeFilter.GetCapacity: Cardinal; +begin + Result := 3; +end; + +class function TOperandSizeFilter.GetDescription: String; +begin + Result := 'OperandSize'; +end; + +class function TOperandSizeFilter.GetInsertPosition( + const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.OperandSize); +end; + +class function TOperandSizeFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtOperandSize), Index); +end; + +class function TOperandSizeFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netPlaceholder; +end; +{$ENDREGION} + +{$REGION 'Class: TAddressSizeFilter'} +class function TAddressSizeFilter.GetCapacity: Cardinal; +begin + Result := 4; +end; + +class function TAddressSizeFilter.GetDescription: String; +begin + Result := 'AddressSize'; +end; + +class function TAddressSizeFilter.GetInsertPosition( + const Definition: TInstructionDefinition): Integer; +begin + Result := Integer(Definition.OpcodeExtensions.AddressSize); +end; + +class function TAddressSizeFilter.GetItemDescription(Index: Integer): String; +begin + Assert((Index >= 0) and (Index < Integer(GetCapacity))); + Result := GetEnumName(TypeInfo(TExtAddressSize), Index); +end; + +class function TAddressSizeFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netPlaceholder; +end; +{$ENDREGION} + +{$REGION 'Class: TRexWFilter'} +class function TRexWFilter.GetCapacity: Cardinal; +begin + Result := 2; +end; + +class function TRexWFilter.GetDescription: String; +begin + Result := 'REXW'; +end; + +class function TRexWFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + if (bfRexW in Definition.OpcodeExtensions.BitFilters) then + begin + Result := 1; + end else + begin + Result := 0; + end; +end; + +class function TRexWFilter.GetItemDescription(Index: Integer): String; +begin + Result := 'REX.W ' + IntToStr(Index); +end; + +class function TRexWFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netValue; +end; +{$ENDREGION} + +{$REGION 'Class: TVexLFilter'} +class function TVexLFilter.GetCapacity: Cardinal; +begin + Result := 2; +end; + +class function TVexLFilter.GetDescription: String; +begin + Result := 'VEXL'; +end; + +class function TVexLFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + if (bfVexL in Definition.OpcodeExtensions.BitFilters) then + begin + Result := 1; + end else + begin + Result := 0; + end; +end; + +class function TVexLFilter.GetItemDescription(Index: Integer): String; +begin + Result := 'VEX.L ' + IntToStr(Index); +end; + +class function TVexLFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netValue; +end; +{$ENDREGION} + +{$REGION 'Class: TEvexL2Filter'} +class function TEvexL2Filter.GetCapacity: Cardinal; +begin + Result := 2; +end; + +class function TEvexL2Filter.GetDescription: String; +begin + Result := 'EVEXL2'; +end; + +class function TEvexL2Filter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + if (bfEvexL2 in Definition.OpcodeExtensions.BitFilters) then + begin + Result := 1; + end else + begin + Result := 0; + end; +end; + +class function TEvexL2Filter.GetItemDescription(Index: Integer): String; +begin + Result := 'EVEX.L'' ' + IntToStr(Index); +end; + +class function TEvexL2Filter.GetNeutralElementType: TNeutralElementType; +begin + Result := netValue; +end; +{$ENDREGION} + +{$REGION 'Class: TEvexBFilter'} +class function TEvexBFilter.GetCapacity: Cardinal; +begin + Result := 2; +end; + +class function TEvexBFilter.GetDescription: String; +begin + Result := 'EVEXB'; +end; + +class function TEvexBFilter.GetInsertPosition(const Definition: TInstructionDefinition): Integer; +begin + if (bfEvexB in Definition.OpcodeExtensions.BitFilters) then + begin + Result := 1; + end else + begin + Result := 0; + end; +end; + +class function TEvexBFilter.GetItemDescription(Index: Integer): String; +begin + Result := 'EVEX.B ' + IntToStr(Index); +end; + +class function TEvexBFilter.GetNeutralElementType: TNeutralElementType; +begin + Result := netValue; +end; +{$ENDREGION} + +end. diff --git a/assets/InstructionEditor/untPropertyEditors.pas b/assets/InstructionEditor/untPropertyEditors.pas new file mode 100644 index 0000000..88a4e71 --- /dev/null +++ b/assets/InstructionEditor/untPropertyEditors.pas @@ -0,0 +1,384 @@ +unit untPropertyEditors; + +interface + +uses + cxOI; + +type + TOpcodeByteProperty = class(TcxOrdinalProperty) + public + function GetValue: String; override; + procedure SetValue(const Value: String); override; + end; + + TCPUIDFeatureFlagsProperty = class(TcxClassProperty) + public + procedure Edit; override; + function GetAttributes: TcxPropertyAttributes; override; + function GetValue: String; override; + procedure SetValue(const Value: String); override; + function IsDefaultValue: Boolean; override; + end; + + TX86RegistersProperty = class(TcxClassProperty) + public + procedure Edit; override; + function GetAttributes: TcxPropertyAttributes; override; + function GetValue: String; override; + procedure SetValue(const Value: String); override; + function IsDefaultValue: Boolean; override; + end; + + TInstructionOperandProperty = class(TcxClassProperty) + public + function GetAttributes: TcxPropertyAttributes; override; + function GetValue: String; override; + procedure SetValue(const Value: String); override; + function IsDefaultValue: Boolean; override; + end; + + TInstructionOperandsProperty = class(TcxClassProperty) + public + procedure Edit; override; + function GetAttributes: TcxPropertyAttributes; override; + end; + +implementation + +uses + System.SysUtils, System.TypInfo, System.Classes, Vcl.Forms, untInstructionEditor, + untHelperClasses, formEditorCPUIDFeatureFlags, formEditorX86Registers, + formEditorInstructionOperands, cxButtonEdit; + +{ TOpcodeByteProperty } + +function TOpcodeByteProperty.GetValue: String; +begin + Result := IntToHex(GetOrdValue, 2); +end; + +procedure TOpcodeByteProperty.SetValue(const Value: String); +var + V: Integer; +begin + if (not TryStrToInt('$' + Value, V)) then + begin + raise Exception.CreateFmt('"%s" is not a valid hexadecimal value.', [Value]); + end; + if (V < 0) or (V > 255) then + begin + raise Exception.Create('Value has to be between 0 and 255.'); + end; + SetOrdValue(V); +end; + +{ TCPUIDFeatureFlagsProperty } + +procedure TCPUIDFeatureFlagsProperty.Edit; +var + Form: TfrmEditorCPUIDFeatureFlags; +begin + Form := TfrmEditorCPUIDFeatureFlags.Create(Application); + try + Form.Caption := GetComponent(0).GetNamePath + '.' + GetName; + Form.FeatureFlags := TCPUIDFeatureFlags(GetOrdValue).FeatureFlags; + Form.ShowModal; + if (Form.ApplyChanges) then + begin + TCPUIDFeatureFlags(GetOrdValue).FeatureFlags := Form.FeatureFlags; + PostChangedNotification; + end; + finally + Form.Free; + end; +end; + +function TCPUIDFeatureFlagsProperty.GetAttributes: TcxPropertyAttributes; +begin + Result := [ipaDialog]; +end; + +function TCPUIDFeatureFlagsProperty.GetValue: String; +var + CPUIDFeatureFlags: TCPUIDFeatureFlagSet; + CPUIDFeatureFlag: TCPUIDFeatureFlag; + S: String; +begin + Result := 'NONE'; + CPUIDFeatureFlags := TCPUIDFeatureFlags(GetOrdValue).FeatureFlags; + if (CPUIDFeatureFlags <> []) then + begin + Result := ''; + for CPUIDFeatureFlag in CPUIDFeatureFlags do + begin + S := GetEnumName(TypeInfo(TCPUIDFeatureFlag), Ord(CPUIDFeatureFlag)); + Delete(S, 1, 2); + Result := Result + S + ', '; + end; + if (Result <> '') then + begin + Delete(Result, Length(Result) - 1, 2); + end; + end; +end; + +function TCPUIDFeatureFlagsProperty.IsDefaultValue: Boolean; +begin + Result := (TCPUIDFeatureFlags(GetOrdValue).FeatureFlags = []); +end; + +procedure TCPUIDFeatureFlagsProperty.SetValue(const Value: String); +var + S: String; + I: Integer; + C: Char; + A: TArray; + F: TCPUIDFeatureFlag; + FeatureFlags: TCPUIDFeatureFlagSet; +begin + if (Value = '') then + begin + TCPUIDFeatureFlags(GetOrdValue).FeatureFlags := []; + Exit; + end; + SetLength(S, Length(Value)); + I := 1; + for C in Value do + begin + if (CharInSet(C, ['a'..'z', 'A'..'Z', '0'..'9', ','])) then + begin + S[I] := C; + Inc(I); + end; + end; + SetLength(S, I - 1); + TStringHelper.AnsiLowerCase(S); + A := S.Split([',']); + FeatureFlags := []; + for F := Low(TCPUIDFeatureFlag) to High(TCPUIDFeatureFlag) do + begin + S := GetEnumName(TypeInfo(TCPUIDFeatureFlag), Ord(F)); + Delete(S, 1, 2); + TStringHelper.AnsiLowerCase(S); + for I := Low(A) to High(A) do + begin + if (A[I] = S) then + begin + Include(FeatureFlags, F); + end; + end; + end; + TCPUIDFeatureFlags(GetOrdValue).FeatureFlags := FeatureFlags; +end; + +{ TX86RegistersProperty } + +procedure TX86RegistersProperty.Edit; +var + Form: TfrmEditorX86Registers; +begin + Form := TfrmEditorX86Registers.Create(Application); + try + Form.Caption := GetComponent(0).GetNamePath + '.' + GetName; + Form.Registers := TX86Registers(GetOrdValue).Registers; + Form.ShowModal; + if (Form.ApplyChanges) then + begin + TX86Registers(GetOrdValue).Registers := Form.Registers; + PostChangedNotification; + end; + finally + Form.Free; + end; +end; + +function TX86RegistersProperty.GetAttributes: TcxPropertyAttributes; +begin + Result := [ipaDialog]; +end; + +function TX86RegistersProperty.GetValue: String; +var + X86Registers: TX86RegisterSet; + X86Register: TX86Register; + S: String; +begin + Result := 'NONE'; + X86Registers := TX86Registers(GetOrdValue).Registers; + if (TX86Registers(GetOrdValue).Registers <> []) and + (TX86Registers(GetOrdValue).Registers <> [regNone]) then + begin + Result := ''; + for X86Register in X86Registers do + begin + S := GetEnumName(TypeInfo(TX86Register), Ord(X86Register)); + Delete(S, 1, 3); + Result := Result + S + ', '; + end; + if (Result <> '') then + begin + Delete(Result, Length(Result) - 1, 2); + end; + end; +end; + +function TX86RegistersProperty.IsDefaultValue: Boolean; +begin + Result := (TX86Registers(GetOrdValue).Registers = []) or + (TX86Registers(GetOrdValue).Registers = [regNone]) +end; + +procedure TX86RegistersProperty.SetValue(const Value: String); +var + S: String; + I: Integer; + C: Char; + A: TArray; + R: TX86Register; + Registers: TX86RegisterSet; +begin + if (Value = '') then + begin + TX86Registers(GetOrdValue).Registers := []; + Exit; + end; + SetLength(S, Length(Value)); + I := 1; + for C in Value do + begin + if (CharInSet(C, ['a'..'z', 'A'..'Z', '0'..'9', ','])) then + begin + S[I] := C; + Inc(I); + end; + end; + SetLength(S, I - 1); + TStringHelper.AnsiLowerCase(S); + A := S.Split([',']); + Registers := []; + for R := Low(TX86Register) to High(TX86Register) do + begin + S := GetEnumName(TypeInfo(TX86Register), Ord(R)); + Delete(S, 1, 3); + TStringHelper.AnsiLowerCase(S); + for I := Low(A) to High(A) do + begin + if (A[I] = S) then + begin + Include(Registers, R); + end; + end; + end; + TX86Registers(GetOrdValue).Registers := Registers; +end; + +{ TInstructionOperandProperty } + +function TInstructionOperandProperty.GetAttributes: TcxPropertyAttributes; +begin + Result := [ipaSubProperties]; +end; + +function TInstructionOperandProperty.GetValue: String; +begin + Result := TInstructionOperand(GetOrdValue).GetDescription; + if (TInstructionOperand(GetOrdValue).OperandType = optUnused) then + begin + Result := 'unused'; + end; +end; + +function TInstructionOperandProperty.IsDefaultValue: Boolean; +begin + Result := (TInstructionOperand(GetOrdValue).OperandType = optUnused); +end; + +procedure TInstructionOperandProperty.SetValue(const Value: String); +var + S, R: String; + I: Integer; + C: Char; + A: TArray; + T: TOperandType; +begin + if (Value = '') then + begin + TInstructionOperand(GetOrdValue).OperandType := optUnused; + Exit; + end; + SetLength(S, Length(Value)); + I := 1; + for C in Value do + begin + if (CharInSet(C, ['a'..'z', 'A'..'Z', '0'..'9', '('])) then + begin + S[I] := C; + Inc(I); + end; + end; + SetLength(S, I - 1); + TStringHelper.AnsiLowerCase(S); + A := S.Split(['(']); + for T := Low(TOperandType) to High(TOperandType) do + begin + R := GetEnumName(TypeInfo(TOperandType), Ord(T)); + Delete(R, 1, 3); + TStringHelper.AnsiLowerCase(R); + if (A[0] = R) then + begin + TInstructionOperand(GetOrdValue).OperandType := T; + Break; + end; + end; + if (Length(A) >= 1) then + begin + if (A[1] = 'r') then TInstructionOperand(GetOrdValue).AccessMode := opaRead + else if (A[1] = 'w') then TInstructionOperand(GetOrdValue).AccessMode := opaWrite + else if (A[1] = 'rw') then TInstructionOperand(GetOrdValue).AccessMode := opaReadWrite; + end; +end; + +{ TInstructionOperandsProperty } + +procedure TInstructionOperandsProperty.Edit; +var + Form: TfrmEditorInstructionOperands; +begin + Form := TfrmEditorInstructionOperands.Create(Application); + try + Form.Caption := GetComponent(0).GetNamePath + '.' + GetName; + Form.Operands := TInstructionOperands(GetOrdValue); + Form.ShowModal; + if (Form.ApplyChanges) then + begin + TInstructionOperands(GetOrdValue).Assign(Form.Operands); + PostChangedNotification; + end; + finally + Form.Free; + end; +end; + +function TInstructionOperandsProperty.GetAttributes: TcxPropertyAttributes; +begin + Result := [ipaDialog, ipaSubProperties]; +end; + +initialization + // Register custom property editor for hexadecimal display of TOpcodeByte values + cxRegisterPropertyEditor(TypeInfo(TOpcodeByte), nil, '', TOpcodeByteProperty); + + // Register custom property editors for easy modification of the instruction-operands + cxRegisterPropertyEditor(TypeInfo(TInstructionOperand), nil, '', TInstructionOperandProperty); + cxRegisterPropertyEditor(TypeInfo(TInstructionOperands), nil, '', TInstructionOperandsProperty); + cxRegisterEditPropertiesClass(TInstructionOperandsProperty, TcxButtonEditProperties); + + // Delphi does not allow sets > 4 bytes as published values, so we have to use a wrapper class + // and a custom editor + cxRegisterPropertyEditor(TypeInfo(TCPUIDFeatureFlags), nil, '', TCPUIDFeatureFlagsProperty); + cxRegisterEditPropertiesClass(TCPUIDFeatureFlagsProperty, TcxButtonEditProperties); + cxRegisterPropertyEditor(TypeInfo(TX86Registers), nil, '', TX86RegistersProperty); + cxRegisterEditPropertiesClass(TX86RegistersProperty, TcxButtonEditProperties); + +end. diff --git a/assets/InstructionEditor/untPropertyHints.pas b/assets/InstructionEditor/untPropertyHints.pas new file mode 100644 index 0000000..7d690a4 --- /dev/null +++ b/assets/InstructionEditor/untPropertyHints.pas @@ -0,0 +1,75 @@ +unit untPropertyHints; + +interface + +function GetPropertyHint(const PropertyId: String): String; + +implementation + +type + TPropertyHint = record + public + PropertyId: String; + PropertyHint: String; + end; + +const + PropertyHints: array[0..7] of TPropertyHint = + ( + ( + PropertyId : 'Filter.FilterType'; + PropertyHint: 'The filter type.' + ), + ( + PropertyId : 'Filter.FilterFlags'; + PropertyHint: 'The filter flags.' + ), + ( + PropertyId : 'Filter.FilterFlags.iffIsRootTable'; + PropertyHint: 'This is the root table.' + ), + ( + PropertyId : 'Filter.FilterFlags.iffIsStaticFilter'; + PropertyHint: 'This is a static filter that should not be removed.' + ), + ( + PropertyId : 'Filter.FilterFlags.iffIsDefinitionContainer'; + PropertyHint: 'This is a definition container and not an actual filter.' + ), + ( + PropertyId : 'Filter.NeutralElementType'; + PropertyHint: 'The neutral element type of the filter. Possible values:' + #13#10#13#10 + + '- netNotAvailable' + #13#10 + + 'The neutral "zero" element is not supported.' + #13#10 + + '- netPlaceholder' + #13#10 + + 'The neutral "zero" element is supported and used as a placeholder. ' + + 'The filter will signal a conflict, if the neutral element AND at least one ' + + 'regular value is set.' + #13#10 + + '- netValue' + #13#10 + + 'The neutral "zero" element is supported and can be used as a regular value.' + ), + ( + PropertyId : 'Filter.Capacity'; + PropertyHint: 'The maximum capacity of the filter.' + ), + ( + PropertyId : 'Filter.ItemCount'; + PropertyHint: 'The actual number of non-empty slots in the filter.' + ) + ); + +function GetPropertyHint(const PropertyId: String): String; +var + I: Integer; +begin + Result := 'No info text available.'; + for I := Low(PropertyHints) to High(PropertyHints) do + begin + if (PropertyHints[I].PropertyId = PropertyId) then + begin + Exit(PropertyHints[I].PropertyHint); + end; + end; +end; + +end. diff --git a/assets/InstructionEditor/untTableGenerator.pas b/assets/InstructionEditor/untTableGenerator.pas new file mode 100644 index 0000000..c926779 --- /dev/null +++ b/assets/InstructionEditor/untTableGenerator.pas @@ -0,0 +1,350 @@ +unit untTableGenerator; + +interface + +uses + System.Classes, System.Generics.Collections, untInstructionEditor; + +type + TTableGeneratorInfo = record + public + FilterCount: Integer; + DefinitionCount: Integer; + MnemonicCount: Integer; + FilterSize: Cardinal; + DefinitionSize: Cardinal; + MnemonicSize: Cardinal; + end; + + TWorkOperation = ( + woIndexingFilters, + woIndexingDefinitions, + woGeneratingFilterFiles, + woGeneratingDefinitionFiles + ); + + TWorkStartEvent = procedure(Sender: TObject; Operation: TWorkOperation; + MinWorkCount, MaxWorkCount: Integer) of Object; + TWorkEvent = procedure(Sender: TObject; WorkCount: Integer) of Object; + + TTableGenerator = class(TObject) + strict private type + TInstructionFilterMap = TObjectDictionary>; + strict private + FFilterMap: TInstructionFilterMap; + FFilterCount: Integer; + FDefinitionCount: Integer; + FMnemonicCount: Integer; + FFilterSize: Cardinal; + FDefinitionSize: Cardinal; + FMnemonicSize: Cardinal; + strict private + FOnWorkStart: TWorkStartEvent; + FOnWork: TWorkEvent; + FOnWorkEnd: TNotifyEvent; + strict private + procedure EditorFilterCreated(Sender: TObject; Filter: TInstructionFilter); + strict private + procedure WorkStart(Operation: TWorkOperation; MinWorkCount, MaxWorkCount: Integer); inline; + procedure Work(WorkCount: Integer); inline; + procedure WorkEnd; inline; + strict private + procedure FinalizeFilterTables(Editor: TInstructionEditor); + + function GetNameForTable(FilterClass: TInstructionFilterClass): String; inline; + function GetNameForType(FilterClass: TInstructionFilterClass): String; inline; + procedure GenerateFilterFiles(const OutputDirectory: String; + FilterMap: TInstructionFilterMap; Definitions: TList; + FilterCount: Integer); + procedure GenerateDefinitionFiles(const OutputDirectory: String; + Definitions: TList); + public + procedure GenerateFiles(Editor: TInstructionEditor; const OutputDirectory: String); + public + property FilterCount: Integer read FFilterCount; + property DefinitionCount: Integer read FDefinitionCount; + property MnemonicCount: Integer read FMnemonicCount; + property FilterSize: Cardinal read FFilterSize; + property DefinitionSize: Cardinal read FDefinitionSize; + property MnemonicSize: Cardinal read FMnemonicSize; + public + property OnWorkStart: TWorkStartEvent read FOnWorkStart write FOnWorkStart; + property OnWork: TWorkEvent read FOnWork write FOnWork; + property OnWorkEnd: TNotifyEvent read FOnWorkEnd write FOnWorkEnd; + end; + +implementation + +uses + System.SysUtils, System.Generics.Defaults, untInstructionFilters, untHelperClasses; + +const + FILENAME_FILTERTABLES = 'InstructionTable.inc'; + FILTERARRAY_PREFIX_NAME = 'filter'; + FILTERARRAY_PREFIX_TYPENAME = 'ZYDIS_NODETYPE_FILTER_'; + FILTERARRAY_ITEMTYPE = 'ZydisInstructionTableNode'; + FILTERARRAY_MACRO_INVALID = 'ZYDIS_INVALID'; + FILTERARRAY_MACRO_FILTER = 'ZYDIS_FILTER'; + FILTERARRAY_MACRO_DEFINITION = 'ZYDIS_DEFINITION'; + FILTERARRAY_ITEM_SIZE = 3; + +{ TTableGenerator } + +procedure TTableGenerator.EditorFilterCreated(Sender: TObject; Filter: TInstructionFilter); +var + List: TList; +begin + if (Filter.IsDefinitionContainer) or (Filter is TEncodingFilter) then Exit; + if (FFilterMap.ContainsKey(TInstructionFilterClass(Filter.ClassType))) then + begin + List := FFilterMap[TInstructionFilterClass(Filter.ClassType)]; + end else + begin + List := TList.Create; + FFilterMap.Add(TInstructionFilterClass(Filter.ClassType), List); + end; + Filter.Data := Pointer(List.Count); + List.Add(Filter); + Inc(FFilterCount); + if (Filter.NeutralElementType in [netNotAvailable, netValue]) then + begin + Inc(FFilterSize, Filter.GetCapacity * FILTERARRAY_ITEM_SIZE); + end else + begin + Inc(FFilterSize, (Filter.GetCapacity - 1) * FILTERARRAY_ITEM_SIZE); + end; +end; + +procedure TTableGenerator.FinalizeFilterTables(Editor: TInstructionEditor); +begin + // Get rid of all encoding-filters as they are only needed in the editor + // Move LES instruction into the first field of the 3-byte vex-filter + Assert(not Assigned(Editor.RootTable.Items[$C4].Items[$03].Items[$00])); + Editor.RootTable.Items[$C4].Items[$03].SetItem($00, Editor.RootTable.Items[$C4].Items[$00]); + // Move LDS instruction into the first field of the 2-byte vex-filter + Assert(not Assigned(Editor.RootTable.Items[$C5].Items[$03].Items[$00])); + Editor.RootTable.Items[$C5].Items[$03].SetItem($00, Editor.RootTable.Items[$C5].Items[$00]); + // Move BOUND instruction into the first field of the evex-filter + Assert(not Assigned(Editor.RootTable.Items[$62].Items[$04].Items[$00])); + Editor.RootTable.Items[$62].Items[$04].SetItem($00, Editor.RootTable.Items[$62].Items[$00]); + // Move POP instruction into the first field of the xop-filter + Assert(not Assigned(Editor.RootTable.Items[$8F].Items[$02].Items[$00])); + Editor.RootTable.Items[$8F].Items[$02].SetItem($00, Editor.RootTable.Items[$8F].Items[$00]); + // Remove the encoding-filter for 3DNow instructions + Editor.RootTable.Items[$0F].SetItem($0F, Editor.RootTable.Items[$0F].Items[$0F].Items[$01]); + // Remove the encoding-filter for 3-byte VEX instructions + Editor.RootTable.SetItem($C4, Editor.RootTable.Items[$C4].Items[$03]); + // Remove the encoding-filter for 2-byte VEX instructions + Editor.RootTable.SetItem($C5, Editor.RootTable.Items[$C5].Items[$03]); + // Remove the encoding-filter for EVEX instructions + Editor.RootTable.SetItem($62, Editor.RootTable.Items[$62].Items[$04]); + // Remove the encoding-filter for XOP instructions + Editor.RootTable.SetItem($8F, Editor.RootTable.Items[$8f].Items[$02]); +end; + +procedure TTableGenerator.GenerateDefinitionFiles(const OutputDirectory: String; + Definitions: TList); +begin + WorkStart(woGeneratingDefinitionFiles, 0, Definitions.Count); + + WorkEnd; +end; + +procedure TTableGenerator.GenerateFiles(Editor: TInstructionEditor; const OutputDirectory: String); +var + E: TInstructionEditor; + D: TInstructionDefinition; + I: Integer; + Definitions: TList; + Comparison: TComparison; +begin + if (not Assigned(Editor.RootTable)) then + begin + raise Exception.Create('The instruction editor does not contain tables.'); + end; + if (Editor.RootTable.HasConflicts) then + begin + //raise Exception.Create('The instruction editor has unresolved conflicts.'); + end; + FDefinitionCount := Editor.DefinitionCount; + E := TInstructionEditor.Create; + try + E.OnFilterCreated := EditorFilterCreated; + E.BeginUpdate; + try + FFilterMap := TObjectDictionary>.Create([doOwnsValues]); + try + E.Reset; + // Create filter indizes by copying all definitions to the internal editor instance + WorkStart(woIndexingFilters, 0, Editor.DefinitionCount); + for I := 0 to Editor.DefinitionCount - 1 do + begin + D := E.CreateDefinition('unnamed'); + D.Assign(Editor.Definitions[I]); + Work(I + 1); + end; + WorkEnd; + FinalizeFilterTables(E); + Definitions := TList.Create; + try + // Create definition indizes and a sorted definition-list + WorkStart(woIndexingDefinitions, 0, E.DefinitionCount * 2); + for I := 0 to E.DefinitionCount - 1 do + begin + Definitions.Add(E.Definitions[I]); + Work(I + 1); + end; + Comparison := + function(const Left, Right: TInstructionDefinition): Integer + begin + Result := CompareStr(Left.Mnemonic, Right.Mnemonic); + end; + Definitions.Sort(TComparer.Construct(Comparison)); + for I := 0 to Definitions.Count - 1 do + begin + Definitions[I].Data := Pointer(I); + Work(E.DefinitionCount + I + 1); + end; + WorkEnd; + // Generate files + GenerateFilterFiles(OutputDirectory, FFilterMap, Definitions, E.FilterCount); + GenerateDefinitionFiles(OutputDirectory, Definitions); + finally + Definitions.Free; + end; + finally + FFilterMap.Free; + end; + finally + E.EndUpdate; + end; + finally + E.Free; + end; +end; + +procedure TTableGenerator.GenerateFilterFiles(const OutputDirectory: String; + FilterMap: TInstructionFilterMap; Definitions: TList; + FilterCount: Integer); +var + WorkCount, IndexShift: Integer; + Buffer: TStringBuffer; + StringList: TStringList; + I, J, K: Integer; + FilterList: TList; +begin + Buffer := TStringBuffer.Create; + try + WorkCount := 0; + WorkStart(woGeneratingFilterFiles, 0, FilterCount); + for I := Low(InstructionFilterClasses) to High(InstructionFilterClasses) do + begin + IndexShift := 1; + if (InstructionFilterClasses[I].GetNeutralElementType in [netNotAvailable, netValue]) then + begin + IndexShift := 0; + end; + Buffer.AppendLn(Format('const %s %s%s[][%d] = ', [ + FILTERARRAY_ITEMTYPE, + FILTERARRAY_PREFIX_NAME, GetNameForTable(InstructionFilterClasses[I]), + Integer(InstructionFilterClasses[I].GetCapacity) - IndexShift])); + Buffer.AppendLn('{'); + if (FilterMap.ContainsKey(InstructionFilterClasses[I])) then + begin + FilterList := FilterMap[InstructionFilterClasses[I]]; + for J := 0 to FilterList.Count - 1 do + begin + Buffer.AppendLn(' {'); + for K := IndexShift to FilterList[J].Capacity - 1 do + begin + Buffer.Append(Format(' /*%.4x*/ ', [K])); + if (not Assigned(FilterList[J].Items[K])) then + begin + Buffer.Append(FILTERARRAY_MACRO_INVALID); + end else if (FilterList[J].Items[K] is TDefinitionContainer) then + begin + Assert((FilterList[J].Items[K] as TDefinitionContainer).DefinitionCount > 0); + Buffer.Append(Format('%s(0x%.4x)', [FILTERARRAY_MACRO_DEFINITION, + Integer((FilterList[J].Items[K] as TDefinitionContainer).Definitions[0].Data)])); + end else + begin + Buffer.Append(Format('%s(%s%s, 0x%.4x)', [FILTERARRAY_MACRO_FILTER, + FILTERARRAY_PREFIX_TYPENAME, + GetNameForType(TInstructionFilterClass(FilterList[J].Items[K].ClassType)), + Integer(FilterList[J].Items[K].Data)])); + end; + if (K < Integer(FilterList[J].Capacity - 1)) then + begin + Buffer.AppendLn(','); + end else + begin + Buffer.AppendLn(''); + end; + end; + Buffer.Append(' }'); + if (J < FilterList.Count - 1) then + begin + Buffer.AppendLn(','); + end else + begin + Buffer.AppendLn(''); + end; + Inc(WorkCount); + Work(WorkCount); + end; + end; + Buffer.AppendLn('};'); + if (I < High(InstructionFilterClasses)) then + begin + Buffer.AppendLn(''); + end; + end; + WorkEnd; + StringList := TStringList.Create; + try + StringList.Text := Buffer.Value; + StringList.SaveToFile(IncludeTrailingPathDelimiter(OutputDirectory) + FILENAME_FILTERTABLES); + finally + StringList.Free; + end; + finally + Buffer.Free; + end; +end; + +function TTableGenerator.GetNameForTable(FilterClass: TInstructionFilterClass): String; +begin + Result := FilterClass.GetDescription; +end; + +function TTableGenerator.GetNameForType(FilterClass: TInstructionFilterClass): String; +begin + Result := AnsiUpperCase(FilterClass.GetDescription); +end; + +procedure TTableGenerator.Work(WorkCount: Integer); +begin + if (Assigned(FOnWork)) then + begin + FOnWork(Self, WorkCount); + end; +end; + +procedure TTableGenerator.WorkEnd; +begin + if (Assigned(FOnWorkEnd)) then + begin + FOnWorkEnd(Self); + end; +end; + +procedure TTableGenerator.WorkStart(Operation: TWorkOperation; MinWorkCount, MaxWorkCount: Integer); +begin + if (Assigned(FOnWorkStart)) then + begin + FOnWorkStart(Self, Operation, MinWorkCount, MaxWorkCount); + end; +end; + +end.