From 84641bff03ae5766dafc9e90e3593d93232a82bd Mon Sep 17 00:00:00 2001
From: dev747368 <48332326+dev747368@users.noreply.github.com>
Date: Mon, 5 Jan 2026 22:54:40 +0000
Subject: [PATCH] GP-5689 Add ByteViewer 'Chars' column Convert the Ascii
format to Chars, which supports decoding bytes using any available charset.
Reworked the memory alignment option of the ByteViewer to allow the user to
bump the displayed bytes left/right using an action. Add option to control
how widely spaced the chars are displayed, to allow for wider non-Latin
characters to be displayed correctly. Refactor the CharsetInfo class to
better support the information needed by the new chars column. Fixed
HexLongLong format to not mix BE / LE longs when constructing its final 16
byte value. Fixed handling of ByteViewer's background color to use its theme
value.
---
.../DebuggerMemoryByteViewerComponent.java | 10 +-
.../gui/memory/DebuggerMemoryBytesPanel.java | 6 +-
Ghidra/Features/Base/certification.manifest | 2 +
.../Features/Base/data/base.theme.properties | 2 +-
.../Base/src/main/help/help/TOC_Source.xml | 1 +
.../help/help/topics/Charsets/Charsets.htm | 96 +
.../Charsets/images/CharsetPickerDialog.png | Bin 0 -> 38486 bytes
.../core/strings/EncodedStringsDialog.java | 15 +-
.../core/strings/EncodedStringsPlugin.java | 4 +-
.../util/charset/picker/CharsetInfoPanel.java | 161 +
.../charset/picker/CharsetPickerDialog.java | 78 +
.../charset/picker/CharsetPickerPanel.java | 103 +
.../charset/picker/CharsetTableModel.java | 125 +
.../util/charset/picker/CharsetTableRow.java | 20 +
.../ByteViewerPlugin/The_Byte_Viewer.htm | 549 +--
.../ByteViewerPlugin/images/ByteViewer.png | Bin 20692 -> 25034 bytes
.../images/ByteViewerExample.png | Bin 20178 -> 24838 bytes
.../images/ByteViewerOptionsDialog.png | Bin 14385 -> 13609 bytes
.../images/ByteViewerResults.png | Bin 20224 -> 24985 bytes
.../app/plugin/core/byteviewer/ByteField.java | 6 +-
.../byteviewer/ByteViewerActionContext.java | 22 +-
.../byteviewer/ByteViewerBGColorModel.java | 58 +
.../ByteViewerClipboardProvider.java | 8 +-
.../core/byteviewer/ByteViewerComponent.java | 356 +-
.../byteviewer/ByteViewerComponentNamer.java | 24 +
.../ByteViewerComponentProvider.java | 319 +-
.../byteviewer/ByteViewerConfigOptions.java | 162 +
.../byteviewer/ByteViewerHighlighter.java | 11 +-
.../byteviewer/ByteViewerIndexedView.java | 9 +-
.../byteviewer/ByteViewerLayoutModel.java | 52 +-
.../byteviewer/ByteViewerOptionsDialog.java | 506 +--
.../core/byteviewer/ByteViewerPanel.java | 255 +-
.../core/byteviewer/EmptyByteBlockSet.java | 11 +-
.../plugin/core/byteviewer/FieldFactory.java | 69 +-
.../plugin/core/byteviewer/FileByteBlock.java | 34 +-
.../core/byteviewer/IndexFieldFactory.java | 33 +-
.../byteviewer/InteractivePanelManager.java | 17 +-
.../core/byteviewer/MemoryByteBlock.java | 43 +-
.../plugin/core/byteviewer/OptionsAction.java | 47 -
.../core/byteviewer/ProgramByteBlockSet.java | 8 +-
.../ProgramByteViewerComponentProvider.java | 58 +-
.../core/byteviewer/ToggleEditAction.java | 47 -
.../core/format/AddressFormatModel.java | 57 +-
.../plugin/core/format/AsciiFormatModel.java | 197 -
.../plugin/core/format/BinaryFormatModel.java | 66 +-
.../app/plugin/core/format/ByteBlock.java | 11 +
.../app/plugin/core/format/ByteBlockSet.java | 8 +
.../core/format/CharacterFormatModel.java | 236 ++
.../format/CursorWidthDataFormatModel.java | 26 +
.../plugin/core/format/DataFormatModel.java | 97 +-
.../core/format/DisassembledFormatModel.java | 71 +-
.../plugin/core/format/HexFormatModel.java | 268 +-
.../core/format/HexIntegerFormatModel.java | 6 +-
.../core/format/HexLongFormatModel.java | 6 +-
.../core/format/HexLongLongFormatModel.java | 20 +-
.../core/format/HexShortFormatModel.java | 8 +-
.../core/format/HexValueFormatModel.java | 62 +-
.../core/format/IntegerFormatModel.java | 98 +-
.../core/format/MutableDataFormatModel.java | 42 +
.../plugin/core/format/OctalFormatModel.java | 78 +-
.../core/format/TooltipDataFormatModel.java | 27 +
.../AbstractByteViewerPluginTest.java | 516 +++
.../byteviewer/ByteViewerPlugin1Test.java | 522 +--
.../byteviewer/ByteViewerPlugin2Test.java | 1587 +++-----
.../byteviewer/ByteViewerPlugin3Test.java | 136 +-
.../ByteViewerPluginCharFormatTest.java | 162 +
.../ByteViewerPluginFormatsTest.java | 265 +-
.../pdb2/pdbreader/PdbReaderOptions.java | 10 +-
.../widgets/spinner/IntegerSpinner.java | 26 +-
.../SoftwareModeling/certification.manifest | 2 +-
.../SoftwareModeling/data/charset_info.json | 3213 +++++++++++++++++
.../SoftwareModeling/data/charset_info.xml | 19 -
.../ghidra/SoftwareModelingInitializer.java | 8 +-
.../program/model/data/CharDataType.java | 9 +-
.../program/model/data/CharsetInfo.java | 210 --
.../model/data/CharsetSettingsDefinition.java | 4 +-
.../model/data/PascalUnicodeDataType.java | 4 +-
.../model/data/StringDataInstance.java | 24 +-
.../model/data/StringRenderParser.java | 23 +-
.../model/data/StringUTF8DataType.java | 4 +-
.../data/TerminatedUnicode32DataType.java | 4 +-
.../model/data/TerminatedUnicodeDataType.java | 8 +-
.../program/model/data/Unicode32DataType.java | 4 +-
.../program/model/data/UnicodeDataType.java | 4 +-
.../model/data/WideChar16DataType.java | 3 +-
.../model/data/WideChar32DataType.java | 7 +-
.../program/model/data/WideCharDataType.java | 9 +-
.../java/ghidra/util/charset/CharsetInfo.java | 192 +
.../util/charset/CharsetInfoManager.java | 375 ++
.../util/charset/CharsetInfoManagerTest.java | 170 +
.../ByteViewerPluginScreenShots.java | 58 +-
.../help/screenshot/CharsetsScreenShots.java | 37 +
92 files changed, 7992 insertions(+), 4334 deletions(-)
create mode 100644 Ghidra/Features/Base/src/main/help/help/topics/Charsets/Charsets.htm
create mode 100644 Ghidra/Features/Base/src/main/help/help/topics/Charsets/images/CharsetPickerDialog.png
create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetInfoPanel.java
create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerDialog.java
create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerPanel.java
create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableModel.java
create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableRow.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerBGColorModel.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentNamer.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerConfigOptions.java
delete mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/OptionsAction.java
delete mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ToggleEditAction.java
delete mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/AsciiFormatModel.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/CharacterFormatModel.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/CursorWidthDataFormatModel.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/MutableDataFormatModel.java
create mode 100644 Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/format/TooltipDataFormatModel.java
create mode 100644 Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPluginTest.java
create mode 100644 Ghidra/Features/ByteViewer/src/test.slow/java/ghidra/app/plugin/core/byteviewer/ByteViewerPluginCharFormatTest.java
create mode 100644 Ghidra/Framework/SoftwareModeling/data/charset_info.json
delete mode 100644 Ghidra/Framework/SoftwareModeling/data/charset_info.xml
delete mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharsetInfo.java
create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/charset/CharsetInfo.java
create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/charset/CharsetInfoManager.java
create mode 100644 Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/util/charset/CharsetInfoManagerTest.java
create mode 100644 Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/CharsetsScreenShots.java
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryByteViewerComponent.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryByteViewerComponent.java
index 8bfade0898..f871a79c3f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryByteViewerComponent.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryByteViewerComponent.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,7 +16,6 @@
package ghidra.app.plugin.core.debug.gui.memory;
import java.awt.Color;
-import java.awt.FontMetrics;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
@@ -118,9 +117,8 @@ public class DebuggerMemoryByteViewerComponent extends ByteViewerComponent
private final List Charsets are named mappings between byte sequences and Unicode code points. Common examples:
+ Charsets
+
+
+
+
The term "code point" denotes an integer that represents a character in the Unicode standard. + The first 127 code points map directly to the well-known ASCII characters.
+ +There are approximately 1.1 million defined code points in the Unicode standard.
+ +Unicode code points are grouped into categories called 'scripts'. For example, the Latin + script contains the well-known ABC..XYZ characters, the Common script contains numbers (0-9) and + punctuation, and the Greek script contains characters such as the delta 'Δ' symbol.
+ +Do not conflate a script (alphabet) with a human language, even though there can be some + correlation.
+ +The Unicode standard that Java implements has about 160 different scripts.
+ +The charset picker dialog allows the user to browse the available installed charsets + and select one to be used when decoding strings / character data.
+ +
+ The dialog has a table with the following columns:
+ +Name of the charset. Charsets not in the IANA Charset Registry will have a "x-" prefix
+ +A short description of the charset.
+ +Flag to indicate that the charset always uses a fixed number of bytes to encode a character.
+ +The minimum and maximum number of bytes that the charset uses to encoded a single character. + This value may include the length of escape sequences that typically only appear once in a + string.
+ +A list of the scripts (alphabets) that the charset can encode/decode characters for. For + example, US-ASCII can only produce Latin characters (along with ubiquitous Common characters), + whereas the UTF-8 charset can encoded characters for every script that the Unicode standard + supports.
+ +In the Details panel, the Scripts list also can contain a short snippet of example + characters from that script (if your font supports the script in question).
+ +Below the dialog's table is an area that displays the details of the charset, which + in addition to the information displayed in the table contains:
+ +For charsets where the bytes of encoded characters should be treated as a larger-than-byte + integer type (because they would have been specified as arrays of those integer types), this + field will specify the size of that integer. Currently, only UTF-16 and UTF-32 specify a + value for this setting.
+ + + +@|?s^j7?4B7%-Yb+Ur= zNp<3}Xyn5Po=ab!`j4q@&DHTH7cIQ7T!^4r>rGr ?HBPTP`JjbkkbL)$Sgq2Cw}Ep%}N_naNBk ztXqdNysxTp_bu8r*Zpa%cuFxQkM|(rifdji<>jv!v0u&^`%9o}-mS*#+dJBVy9aHQ zss6mVuP^+cRrP{^Q85&cYNpcj%r=)d6qiX4Gz3r;+24>;`gq-TNwdM-eU*;2oGQ(V zkB|R#YTnfF>`Oq0Hal0M@At*5@1H?B5BVctDZhN_kvFE=LV(*(B@<^oQ+e c!!1e9bp5Re08 v v&x4JqEH++87J7{VX?o$mt;pj z1meN^(JHB57#4+d3%oZo{ jx-t(UX$NlG!O+fzh zQqW)lg(ABX$_g?-`sc@^hapRdw!qvgFLd}jsZQ1=OuBTkt-#=WOdz`C8!1buQ{_m- z&3%!KPiqh@3iVsRA+b6@q@goFB;6~$^rD2wk3PTlG3<03AK7+mkVhpj884A?aHwIU zOi&KJpv2)yNluOtr`TsorEt;vVO6>-gbr43V-u5Aa|l)YH%Y8<_j}AlF7(GxP{FX| zMU57vHLPYwoZ7d06?xKE8DA?ZD&h>^@bmHUv9VzVc-n?Md`K_cP$O3Ah&T3A =?I1Lft3?)y*ywArm7< ztl5=L6nj_T1)zsj#6PKHeLd6q>&RlMKDRtlAN$ONcBwbhbY5|agdq*qxX7MFKtKSX z7z_*y+9!(a`){*zUne-{KH(`@FjusPQ?H;&eT^u(D7k>Iw`$C=uI*Ad<#Y8RrPbaP zn=|&OgQ6*oPqpa#a4znjLCyVMI4HT14m oq9EHdS27QmtrAil>C?7uKDh9T zB1bL9hnie1d(?ug;fIS(L~{FfLYz4~3QB {s1-7 zm)Jl{E`Tpe+)f>qJ7(Emv1w3t<0kmaSp5QE5g+xkpHlS4wG#jB60wD Jtu|x6-R+eR@{)4kh0=FZg{~;E;&a81?HRv7zWiIQO+7&9wUa%u;iRTz6}W ziPF=~r^|K22{?t)q()4a3}K86Mypec)@U`evBhD=aJn@{9nwofMRx1vyl2h&)dFcV z8WCQ8^B(-(pprB4u2V+cmaJC1PfImhMfd#1(X^G-dw*Z4kaRJ KBJG6Xi zU|h4*8D<{$@aIo_{qfhQRmH2_Cs*dAKf@d)?8UDnEfcW)V9`Wg_7dX}4&|9Km0ma} zvg+2H`5&%%pBoPvOkliwF@3g5`KB!6^|K$k)sAu}W|knAMJ?0b{GcOHR#xWY=a2fb zw6v5EAnGTuvl-a`%)LQD@L+?9%VE>mfp}xxD+g6=Izpk#D=q)%;APrO(`K3lx48+; zDD43{;V+R_W^doa8dJfy3@uZan~#1{Z_-!~sJ=oaZePo9s8tFSJGr(!ow*ecRm%;# z?j#`+;g5N$t>!2`CA{>KLzta_x3anjPK (`HqMYW|Lap zH0dM@??=+@!OGtLvw&5VEsh=epwv*A{uf$=9G1Bta;8P3h|x1 BUo4U`m_8-inZ;$YB0}=wX_zGnr*A=eFUt;A;No#y$KC?g z?b({~mKF+T=Exp+Oq9#g`$U9L#XC)hNm}9?9=K2=$}JyA2laEjw5l`oLxce?Z#$EK zidZ=A#+!D#*KCLsax1}z=I}ak;Nz-bwaZSRa1Cnye1Qf-yW=G>@7uD_`GaWd61RD@ zG)^+y7v^(776?1H!$+$S4<(4sWoZ;COOLt49N?PFlxjR~hp?^4O-^=*6{JIdio!ti zB# DF6xuFM$x#i z`n#hLo<6tYr+=PcvWpkd_Ue#BjQX32(Pbg)<$^EIvw^!6P0zEo9<2_@)P+ti6YHj^ zU|3q%AJF>x`Y~yb6$5S2^&=^Vz9`gGr?DY{f}~azsyf8(J57&1%Hx?B7T_}Mtt`T# zmJw&j=J{m|l4t|HczYXUkw7S{ZJ0nRhwbps3Z1DC)vQU*Y_+`8k@mjx9%4u7b=;3A zPwfVLNNk%9v9um>LjCBsC|R>l?5f zk%ykr#EMMm05AIC_GMyd(|WVhF<`8-!9A14`a3PF0DYR(^vtP4W%^iv@J&WL;j1Fa zGB5q>3~0F1hdU+NcwG378VhA(V`I9)E=X `X%M@c39(N@_6#=wQ{UQIr%-#Vlo$ z90f>c$9h#b?WqA)-<9zn4$*>W=fQv9Yke1z>q%N+$rn+PxQkKfgA`b`go6{#$PhNO zftP_V9;xK1-H ?TdAg!m)Te62 zDXjNhzm6zAxoP_6>tTcCdUwq&jhOF6U%KWk-iL%7Rz?gCDk`z809h>l@DS&2zC;|R z_?=xH8=2&A&u9CE@0cZiR|e}I=16fK@uy+XQM1IKqLKA{7QlAJi81GLTg1e4C>j4m zpQB>5-m9;YVal)yhk{2=(=Sf;IREBi+|2UlN`z_?!?at&+_juZl4yoQ+ozKh9=F&q zBAr>a+fp(a77mr5kZh%584*TWO3cuYRr8Ff;Zm)#b5{{|VC~BMu@aV3-yx4oEFir# zjitr#KAFQxzNbPb(!AA;pk3}$_dJw)&(|GL%Do?m%eNM~=VcbIK2_KwaIoACeT53T zo`lj4wqGl !uyHcChjZVlVNI3X~n6pWN=IM6J?$QtR_8kS3Va>3?3v2 zzER|2pS?D-;t4niPFA6BQHZjjKx}H=xzBonUuFxw%&;7_42{n-7Jv4#9*-v-C>l~k zuTgTJj7R-m`uO9!z52C3tMt5)U6S+leb1D4JG-uTg29>>ZvO-8b_nXHlkWO!plgG+ z#VKm#wvU$;1evt+356r=FJzu@?1sjdeGofV(kksnYxcBUxUE4-OiWBmOWSsl#Av^E zpy62Iia|vS`rsmBhC_(Tg@U5A*+nA+KO%Db-91+AYQ}aHB+)5b!TjUcgVed8QoXFW zFR3Z+F+bmD>u$CzcYC3uh>{ _7FDM%q#8kMJ2F lQNsup{|=S@YOa}TG&M^p zOL_HCDzbEcWwtXZ5T!Mpt&eW3tbfm*)H++6V(HaMvHKI>LUQ~~CP}g2v=lK=sQ;;J z0S UT=-%0r{GyUVdGPHM1fK`8HA^(&QZ#5aaML@ENnQ+*;+ mo3Yare2QEW`Ca##+u_Wrq#bNeb5nuLS|4K3pR`}c%Lg1H|lNS>d+h@~fH zB-{c@1RfRD6~L8lWbFQA?v|Y7+*Ke(sI7L~Hh)TWU#p?XbqFP N)m0dls863RLC6;PSg*2Gb3U c2K`e=YjyByh9DK5u_EF23pg)HbI@M)?2V)Oa=`S!N;jLH{*uRrM3^Pyp2!U?&H zU!QCi6&2k#>I;~><<~3BrX8iCBIf3_*ie{auV26ZKqdy|C)A& yVaQNKB9!Mkg=F+#AzZvN3YwKR{{B*BrG# v4VGn;EA8P;Isgjc*m%dKn@6%}Jg@
-KFOL`r2-1$2`oF7Fq$(bM1DmkhtkZ&YN&aj;t+`G11Sf >}^0Q?)#;BG#W!3Lii{0DN#W9$_F^SycsCC~5!zfk!mr)JLnkLm6;5mzAENCS_Fb zEKm?YLh Et(EaGyl=F2c5ryrP%mKIiEip2W)3F!>G(ENr9#C< zu9QL(1F3=o+g|z_@?}=zfTB3z 5cK=oICH$5rsPqyo1F;ksR4R2_QVmLH0b`J=x!f zM@C*@eFwG;+xOds7mow$%$vS(MI1W;1(dUs)T;#UzOS$EaX1gi(LeE|zPd6Po+FBo zQ&8x*_TDKQcaI%KH$lZ7$Q=8VrL*Zm&|@rQWFEJ@>m4M%+hci8?#WY*dI65n-Q^Zj zGU@3eI%f!%!xHouJB`9;<0P?AoK;+e0I;9F*^*`^W;Ui*&2|QK_%C71$l1 *gX;8zQI34Q!KQe&~%Uvw4+x~-Xb?sJb}2MRF%BtDN~ UM(FrLT0N}ukumS-hE^}6E6iOJ0;Ad5-cx{i9vM}mdvhNi6v3YGB z9Y(UntOcVMLV)v9#b7D3r@xU?<( iO0uQg7t%_v12G)JGCN za-Y~=RiNbrecSvJor0H1`yh>l%-o*gjL^~1NngW8kD|kl{^N!nh75|YB?HyHkNP+j z`H$B6qPLee3=CW$CeuJu?T8>*$HA%_QT)-E$)eszEwlJc&uaDChoQ{EP?3DQ`Ff}I z{>N6_F?k>*cUsT{jZO5xCj5Q1%huFM=3C$EV3 UFPAT8}aixsEj08Oy;UB|ak! z-kA~=_3u)-Nfhc$%%%eg7frfhC)tnzitD|i0X$mbqF<=N=SCxOPmE@Li+5t8zKbsw zfaS=P6q}R57}U$jV4)CY;&)wZ0>Ad;!%R{W4f&>t4TmdtHg!bd?$>QDMuLf+)V?H6 zb+ur{ve0&r+n{mEc=h?e0;8nko#5topjo)>OsTR~S5?_e6gFkzL8%4#!>5qp;^PND ztpM5F+A7*>tgk;0#(GjpcuO?$$#~B#W5z0IO102g%=!s&adBmhz~ 1x&H38`}_M~8<-yATk|Lh0IFOyD<+`xn(|Ls zvH^)VaQXQ({2?kpA!}cLY2fX;n{}JlEwhZ>A=9eRO$nu~-iQ&Cq!W5KoBlQf4B{lk zuNOa#?W*o5H&B6*=%lgF8#bTo^I}ukWUd1;L29arfNye1haJL%ysq2kW4Vb^EU=k! zwlQByWkgULy^G8SKI4%ATx>g8OtlAf#018gFBSMle<)=jU{Pm&`*uh>S1DBkgHcsg z_41o+mQsc|7{7fnNyG?!^9T0UTVgZ}L73GS0NDIYx4QH 3gz>d#X@A&a0Ll@nb|JLDa9#P* z)Ra0O)sslspT&137=;_P>he`n(fyh=@?By1^e7V_{o8l>lqFTI9j6*qxM=>JWgJqv zWsxA%ECC-Jv{bMcW8u==4YiYYeTZgO6Igj(Nm)Pl7g;aQal#Pe%qxb!?;q6n;*=P& zbghu##bJPRWL^NuG21ZolOYg1C^*#4u^=Rh<22IylmxSE^e{*`(UxCB8PU =%IU8%}(6-dli0zISm6L=cqL<0m&lm~T;e)!jA z@6~c79QZ!MV(iAV%ou-6XUf0T^TT|eCE~Y2{;kD5Vty;(V9^X_GOC-wSM>T-{!lY9 zMWW;^`n12l1twA?n0-rUV4TMfMDZ~Fe6Dpy3U7VkKvVhD2>2uF75`ivhcw((ea-sT zwCC=oT-Cz2^ccrSTnlo0ad8oBTei(IWB &bfUFk$%i(VlQUe12QQ;|q2NeKqY) zrjRREYzI^25knJ{_K^p*d%IO39=-_U`}LOJ#5d@!{9c#ZY{0DZZFS!X zlb3xGPR~)vD&{Jyao%d`d8#=Mq#7l)h-x?B_^zR_YMqs?G<+Nu=f55lb-2FG=0OK4 zahXLAhlV@+vY9?|%I&Dm&^Z8r4Y#{64(PE7Jo_a~_TtAk!GTobZBAvWcc(Qb=b^>J zLP1*b8fKlZ)(kBBK36bP(h0CIcmm|}L*ePB@a5yw3?aXu@La9D%f$B@S!&+{uG7aH z_#FvKjI`3p*x9i|ntFQZ`T6-fBGcavk(!M@NIfjG4Eu;gHeV%CFIKLz>9z^~s8OxH z2QqiJ3+ElF;LgPsk|b@({mYEzsWhMe^(Kfxv05p;6H!5P`s?Nxi-Nk_K>3%px-~Wv zr2(- OxgyITTI , the Code Browser.>o$M=Y{=DvXa)`A~ntlOBy$n19?_VM*V%T!Dd*275|ma zq~q$PH#@qecohX#hBxK&TpCyu;=peS{LEbG+5>v#M;ojr)vkM8FCknnyT1Ibr6H^& zPBav2oL4_>4*YB!miXU+af8$@n`lpK)<~O2W-1_k8a9FgR7U~T{T&Jcj&NX^U;PrB zqLR@n(pezz-iFR9)^+2wd#6$+dy4z+Y ^4DqCW-=;iwJCmg{$lC1}|L+P Jqg#p^MW%J 05k-k{DWvR$4~@|sFu`txtkc$ltVH9KazB)NWX8EB(2 zBV|B+@>~rBw?qRy0E8j&=1$MH(rNx}Os`cZMJgKp?NH-Dg7>wtTe8zL=@*$$fAQ0P zJ2#2t?tMsdER_3x^B 4y?~?6om-uKQ?W;g9?-YopvYJS@5m{YkJGVL zAJoZ$496O#wx6eb8yo=JRxzq?>3pWwnXW`S65ia8OpG5VYx`jH+_%zg92D}x58STg z1F09!G;cGs$8Bl9WIPaP+vyuz)L3t>)^Kv~h?K@p?R)1^Pe9F*AIi6d#3~o(o6X*> zAagRP1@r`QH+Fa7t5?b5Sg;iK0@)(68%*=rpyuVrASc6YY$iP&FvSsCIdaic6d-C^ zx}E4M$qkzV!;M!a*rL4i@lJJ@kIb4ixmn4+n&z2(g)QkNS}x*}o%`0=T52ter6x{t ztq`<&k;-Sa_kb%Gun6(LhcR%m4ned*#4c=Yg|e1Hb`PQ~A$_DIIJQvdbT$>bcOcNw zs-KszPKW-{oeN+Z)h?g#48QEhz3NoS$Q0=;Za~W(%vLRvsZd9ShdhW@L+BN7pXjv1 z@rpOOJ-!3eMC%leo17|ulW?xG#Qu`rL+c{|GG4~XXcy5vzk)^G_#U2g5YNA|LdH~o ze3%M%9katM+)eg)cXF?V;qq2A10!0Ru%@xIXjwDX`9h`O+XLE^Hw(zLW}u`aHd4M1 zknz3t)>Qo$?Hz){KzGmL6D*VFf}rewN|Har$%@+x^^I9*{2dF5I!Lua<8(59ez4U4 zLx2O9_JabK{;j}Ov;U#MHP<4p37*?F+rj~eGAcv^Qs_WV&wXEjcKWAdr}7GqT0ock zl6A~^9ZlZ1!c*IGX`^HO3MK2YoOvL8@}24cv5V=KzFz`9ga$qfM^iWTx YY4_ zDZ+H;-SRp30s6F{Pe)t3iCuV!2=JJ?uU~TZC{M|Z%bzG`KLG5y@fxPmlK%# 9_Qk%#s24cp>lIu+5! zcdk$zP<|8|d1HE&v)O*SJ4PyJ8-k6SKVA}?lthEw1R2*1+|Um#`pFZ!g4ubGvl w z8F!^uw)#r~s2Pb;NfFdm?;@+S&nDe*b!Rt+oZ5#1%y+>3W{~~DA>z^z(vv}Q@=+d+ zs9tUI)oSb0dzM>fS=QF0#!3 $-Fe|q;CcRCa0-rABulCjFSH0vZ zz4M>lPphWjgLoE6_<{1O0r|bu7Am5bfiXwzdU^0YHJ(8om8=uv`YXzlhuIhTB`t&2 zD%>LfbNjp5fZpC-Ffuvi$%Hq$t;BlbNGKf?B+_v||3ZaJ4h*C{S&1g~&pP+g&hfUb z*o-t$xJn$~3+X;r*b~tzP>})jj!)WBbkx?9#5v4x9xulAW_oK(_iBPus1+S{7t31! z1>sQ-T1=6i@Z{zIoBe&!aR3KH)HynBzlUe=3ev`xQe4L20ZA+y !5hCu!JqM3S{-dymXq{iM_i9&W zUa~z0{vN)CXd%$mV^Qz@wzwt_`u(@h_UJFAeXrLOB*3Qt{mvC}M*ZN;JtxTonn)-e z7>>dJ4T=BrJH;1yk!mY;V;hW~K0>vB*$z&Oc%}V*pT?Wk_sRrFNJuX0HffL0_9OV` z9$5IlsC*n!l4hH(O~zav^P$DYW??AeFBSW2! Ww{BeD_Ugl-`x#(RHMX|51v&WDr2 z?PUb7WwpI~byGi)p@g3 UvU1xWouzLRy0X|yYfG24`o`l^qz_6SaB5yY z*g4>OS2M%5IuUG I%D#madg}fgp9&6(zlDmU-iJtPEq-O_Cp@1cilRBvm*Y|J65iaebOvL zf2l1wDFF?5n<}_R*$9`2WV{?A>?gd~5pt*~0}{0 j-XEV#^rIYEgM- z*%4((&_6-Y e>dvL|7zKy`z~Uqofl0JiI9(|99#mzfSx)K~rLP7x7}dQybj}-f z06fO>O78nr+c({|ym(#>es+eSf$ T=;)&K{d?EBrMfR zL1?hQXi=qkB7e{neiz@eUP0JE3KOEju{EKi$N^u^vSr^M%Qy7q%DcwyFiwX@v|5lj zWzuJk<@nW66s74;9v8{M!is9U^_uckM?ON >>Dv1)95!AS9E z*$iV>DQjrhHEX4|Rm!#govs3*i1NK24$5;SkM6fWZ&sKuDwwgUZ*acV_DJ4m?DTw4 z>2{Jv2<=W7U+rX=NT=3!EJEL5W>9K4$o%opbXFw(zgJGc!>M#}))>YU21@5CZn-@% zV2o|f=3Ck{B*~JA9%y(C1fyu$}gr7P2XEcxF z#-;GbW96ta^|!oyon16nBHCE&Moe~
}FaP&x^-j^}9^2n)PGI zqFey 8!)6!!bx+G$g1y3H6&tI3Q%&Ax+_{~mmK4)b62uM1 zj!w0rOj9oO&(^Y&9k%E3@}+%wBRBW{To2O@3-y}2_8cRjnAE2J$eT2fW%n|dtvmTE z0oB+PFK#9*{+9%|WJwF3yB3U;lT^urXITVoFKH${e66bp(Sl^z!F(4WV -;{_?Q9bz#{VU8V2erI(&-zw_VZ{rz#v7EID%{MWT%3l{#m8jq zHcpSGe1Qe@7zKh%j;Pi`Vbribf^rrO$3aAuDH=P3f$C2^`bE7=zr{DKS@?hv&TiFN z;O4tomTnCM! }JA(enx$4Y2ONbr#3$x2S7 zF;Ux ze1<_lKt~ryh>0?0{%~KQHFvwAp4U{XbyTUsJ(+gnKpO{(YNZgkx35s1rKBJ151RZe zV-C@icQ#ta8dpdj@3H?V-(X#*#fB{oGFC@XDMQmE34 K{GOiQ(J`74KoTA!snPaWl%u0dYTQAw!epS!`bd2F*yME^H#AZ7$X?&WX`@P1? zbx_TE@-1UYBIlYTcuh=A0|Nt#$H~O-jyh6y!=WDirId%~^avqP9Cp@h&_hb5mmu`M zjtT%9jeYs|o7Y&ob&s3E>*7<<8{)~tr5YpE`iWfUF)}j6RdzT|n EB t-&ECAc=* z#}5}6a#odS$8+Xi;P;SLhhI7h1E$!>vP^S;Q@Slm;WU M{wYo6cLh#V-QA zK8w)t%iTs{g=g(cV1NED9d8R9pbXP)p-S*iflC@}qD_t~g8@Zp_13f@YoOf?HIM!O z!P(ZG7ni`EdXKYXE`S;BgC8sJ!3dSGb!%4|aHHMF_+J@yQp4m&_2)iiS-?-_{_`8d zw1HB)^!09#Dsb|06ZxQuzS`)1e(9S2D)`?m%lm`>!Ls~a7f$`Z!)XlF|EP4U9;hCd z14p)Do3zs!Q^ sgEp5m8dLa7eZ@!=%BA}UxJG_))fV Vf1uWF_&pSl?u?z9{$tx(*W+pFbn)9{#B;TP3S{ z|7x{2;gh$~AB*xEIa7t6s*6cnks|Y)8?O`qocF!S6y#D4T=L9P>9zB98X{nY`)J*u z%}OIO)#|+h0J;bM4K(Br_YS)$uWH%E{8`U-O5K&eZOS#Lt7UG!E$Rfz#)hjErY+QS zkA@^X2#`KKm^j)-MzH>Nar{OVz>%1=A^=<*$vnWtq2}}uxaF6u`aib3u2pE=3SXOR zp=wP^J%7-yiW7gS(^yhuiHM|$bX6-o7^4*2&0Su1yri1jLvef@mU3X`tF&Q1Z-`%G zxD>eAv@vA@>B@&7o~l6qvOKpw#azSH-bK6qH4!4PEW5dEHSbg&>sFsM3X`}Mbd8v1 zZ3^aj{9s$LuMl{QlYxwp!BrH}jG}i>_b8Pufj?0`P&@-F)knY|pNv9hsvIp`3L2cM z_cQL;mV8$jl*0SRwqZN6>wI2Sxq_V8_9~;D4#W`$DV@2?o4Vv~z6Zr^mpAgG2g;|7 zBeE7jf^+ M(i!Di5k(MQGxbKq}wG~oCI?_+wkW;uA1Ycc$L ze$4V_nt`6iOlLI$QB+!_rz?=`tQAY4aQ10e3pa~@2rd)jC@>NoUhw>JXKb(ByEAsy zw}3whsL!4%y&Hh7atRY7l}pA-l>Ic WHq>$jes|zqexvR4`L3(Pb!RJ) z;Q2a}-fIpXA$I9?x%>Q*Wq-l)g6jP8G95YZ4Xd-?`8BSnmf<@0(nD;9qV|jh)o{ z z{ %HC*^##p9m0A$!Y2(&=AIdrQI50{ho9)O&*G5Gx%MN(wrAr9hQ2V)z6K7wN1=kZ z+@y9wE=OdM!GXue$2-#%BJ}51R}@ShuU~)5%(QQNk={i2(0_h_q24IXqdyK5vqZr| ztDlu!09}h_;HVNg7e`xS%DLT>dE>MuNs8Tqj_z)p*rDKL3KQKbc?Qxjzd#byNg$ zo*OP$2%tU&yWM_!75BAt{8OY_xO5HI%G}nh+oifwELr!DXGh-=>aKif?;j;*g=)=b z_WcOKh!pt6tL!Zu1wO%S|D%Q?nt>3Te61RmQ{mT_qtgyu__cC*G$`fUq2RF1?0oso zQZxn~CHFENy+kVwy@HWYbi)Yx QbA#wn2>E zdiP1`Q9CK~$za%(kmp;xF1vL2;Os))wS`EWq4f{c$Eq2|@qs#ul9aoyP7S)qC?Scp zXC(8dMBumyp56a$RkrwVR^{{m%c?B(AFayy-Yz~hy#2_B)S^(nuuv8P9#gqU8|us( z=v1G*w7b#d55y#aL!TBFxGjYkR)A;y_fZdcE-bw?VKJ2hJ5B>OX0CUmmOBB{z^6Yd zAnuZS3zhqU|EtoyyW4{3T`^h&)gNpmQFXW)rFPyzm3P#+L+rb4K3u8^aB88oaPcx7 znpH}aQAhk!Z5%>=+w^l@lx)}Eg!j4#ou+0 I@T&eAcZj%8X7LFk?XBHCOyLFY|@Yo4;Y3S8p%l z1!C4EU!CM7#P_Z7`MMZhM^<0YrO9 r! z9H=3KMu=}HebrNcdp50>oQO#!!{PPy0G#PcI51aaX2(x2OxG&gH9HWURQ9o-dkJ+m zuiM$%i;RL|QXmq-ycWS*MAnh!?9TQ3$d!Ggh537B1px%%1mZ)%ww%(bb=MqJ&C}8d z?KeNd?v4dAFV8jkykKv(TOTy6G6}KzKbY=0X%~DK?y!&rlTK!86!lamIL~5VB-5*Z z|2y|b;W7pv4KW+H{(67Dvd~#MW;2CC#4Wy|SSfi|7y|yN&3zvNR^edrw10OuFYPe{ z9h% Gu0)&IBbe6TSK6aQxihq%z|*OyTgYW0o?aFtzu z`;UR>#U|k5=I;8|ftw8%dSxTp@!KAHjnT3FzZQ<|3Dy_2(0 z0%_FGX0mt{ct%aEgoMkUz`)rpnq^Wog41oE0f7Pjj1QFP@E6d3pe|O>?~{4bv2=Bw zXY#VLE2`kc=n`<<080i60lQ*5YYe0A6eBMiCh~`iqjg+*HS4B#FpgqA3xdy)RnSg1 zzNgmTyOGQAy*Qq(7vQMWDB1rvUvkxNU$VG{#su$P?aO*EkIZ>+Qhs}`E=e66t*@!7 z;x~asH0w`ZRF&sg@$!a?E<5FQ*`y3)%c#uq|D#ObO;-6qfu3{k9|gnLTyQ2iB{MUV zso@$^hD2`^ICqJ7T?^$5z)9-DAD`!e*FNG2r+m5AlqwX0IIMUZIPl+KJ;{14rkU?! zaQ!{df3qo@z$U2wdbAm8cSz4XI1tPtXknR{3PATK6bXS14DzZjHBjdyr=Un9#bwmN zBOysmNkMT~+uZEVPESt OCxJ;S2Rwr$~36htji2`Wld zL?xICkemfXqGXV$P-Mv&L=X@JBnS!;iX0_J5k#`2B4>~wIp@qb3+>*0&fVv`_uS|F zyZcA?wkeCM^{zG7m}8DP=BdC7Cmrwl(%iM2lxBPhUF bodM{ zqM%EH#^RGXJ}>Y0io8ma#(dAzs=$)u=p^^1D&EdjPI&w=lG{PO#5SNOtF7FPpu+Dg zPL$cvxVN(bz2O%vj-{sJ1i7&aZYl)z!`HsWIkifE(QOPPXTkNvRnn@rNSW;$M36ma zJ4y2Iq{DEX+N{s^WOuy%hl2R4%fWuy>|cGAy1EHDIrpz!{f$1id&IoY6M;6J#2VN3 zdd3_78qbM}H^lV$CIh%mxdM{`P~L_&1W`R#3ZA%d30Wk2@7COjH0YlP%|4h+#Dmen z{`y?stJ~#X;4`3FU_x;cyrLFb<9*bO@mqs@e8ep+QPs`h;PBzxy%?wfA37Z1zj5mc zkk<6{duGiOl8Msq`aA$) ghaRLQ{61`GAq}MpWi?z{G$FZ z!-m=U`T0qgz)P?db8R$I!r^i4>}?EXXq17yg~!^-`vxpYR++Ryv7KUWQXBIFW+eSV zf_k`>>o6g?0?OAg-Hh8~A2gnKf3eAciif5qb?7;$%xfvKQFG{MYd=f9l=NMWl&RZY z^7-$cqK lNl@$&+MRx_)YzYK>qsjo^sK31F8%^WcHf zJ?%g0UZ#-Vw70L&6FSN8EDMt}?}cERd3G2XU~~Cb-S}mUb8M*g{`9_upubo7lI!}s zxD~iBgo%eTONfe|HOwP#=TJHqBQq0MPQYM3e|==XveE54)%{sYZoA&v) z+}AGI()sSekn@glON^ H-VjV80?vY&~W ziGZ*a)Zm$^`GiPsexDsWAK_kY>j#VUyJu#_t?u8rkp%u2PznBa)UY{{yCJC~y0hH- ztgi|^lBd-{^@?^O$|XWL(^6NtPi&@<&-846 zy;lU=lxr%hbElz;#qrv?( |*A5`LAc0jDK&kK_k5_rcH(a uusTvr_IoviE*+*hlHuXd>=n%@n&K7jDI%%p>F_5)Ph zZJyuH;I1pPmKRg4@eur2Ob6AJ=>&2Y2EXn!vJH{kTAPK-v0|Fwsn8D)^6z<_yLye_ zm02ddH#nv}ddS9B0Ebkx8r*#f3kx+{y%$iLb@jZFHTGQt0}(xUzrj)1av*eMWKzyP z7Jl*KMM|Z&*`877e>UiV1h$rUxu($pt`nuDXd+NJj~K@g?MC1q>&?r6%c~)bW#-3w z2KY{Hc>Z(nfgm=$3#TT}Y|ws~j-Pi%!5!Vsp`YS R~^xw!!2l^a`Yul 1?TP=G1wEogt zQ+er*=F1M(L{x8JL>v&H((A)n?(w4Uv PNS4-nc}cPPS^qZR0>RonB}H>Y-YP# z(v)*>PBvgykffG#{;k`g(s$v??8!&f{r&wnpCo|p6pyV1`bX|vWo8!ABE2vDvrA?7 zA>OAgrDBkPx5kS@^z j?B|9ZGxSNdHKC#kdN#Yz1at>kNFeoA1fd>&bo&kp=uAntp-M zS5S~kA75w=3_||2#g#vk+JO!((vDq>pl9X6^A}#8&6?uY=GpxhhJt5t2oiU_fGXf9 z20P4uo%Q^sI5#6Y60S+TI6~GKxbxJ!h?g(?rU}1~68<|aZ ~Oq<9D;P67>zWdbC*<=VcFFDP~wqh9_01H&XQkF?9ze=Bwm`2PZ~xId4}{AX#e zyZ0Y@R8%hhHG0$cch4OEWwIEp-<@^GULAc*UmL@}Kd11g)z0`s!(Y91 BYnKQV7fYLj7W*Ki*3D<-ND yI@nsu?w j~R`} z)UwzVqtl~kicdGl6JX15V@cr~_rQxbw(V*SjG}3OF|p zwgidCcMHFKfzRuZLEaR}&2ad}ox*NGbisMDn3w-pMa)Eu&V_rXpB|SuP4A`$O_&OD zZ`MpudKVS(j;I(-zZYlkx~)<6KFlt!?J+OAtn1siZ IH68-{Kn!QywQzuIdB}xXE;jo_)>l%WdEUk5kQ=ER8ev{^c2~n?weyyLl zXVU-|S?J7Vr_lv$>K8%g1+Jh7t6vYhibTdlk$h~n4i18JFHXsQ2L~}$qwW{rMstI( zyvrn0$4nX}sxmx8aBgD`fGnUL(D>g1C<|R{Ph@2a53--vN7{{l1f7u=lhScq>n0yH z$-^XXj}`qMlhh{P0SmLA88 ihvJVamY;hWObN9N6(VHrG(C;;7Dk2H(ST!{we!1=;+O=8YD6gLn z(~r94 vP)mYkRb86g6I$Ik~a z)SH+xbYj!ESavmUqj#ld#N5AnobpYhM19>Hs;?NW6zdAOHPXjpp>&7&&K+q#Ilkg> zi$4#wC5iD)Fr#wd%;{*zJNlV8IaOD4|2*l#Ul}PPyR+}8H5n32)OBt9^^m*vqEGSG zu*Wr}L5-lzipaOo?tMp=3jAt@eiS*URy4%Q7M>?0_wx09`qiF=1e$wxffHJz&S1&Z zsbg#THYB5&1h`pL)C;X8ZQMwPYiEGqpye)$LcF=D%$)Nbe=o3oXJ_+g9Oquwb>zAz zW#+vl>9Iy)!U8;E6Dt2NR*d_dYyF(Y2}%d+?p~{_mb|=WkY(jxI(N zO`vHE23pXL968djwZFRs&3IZFPouxU=?vLr4ix?0WE&@!8a?|a |kpvx_{-7iw1nOv(4 z@yBI6ts6}qFWQRlw1r8xrQB)rmTp^?ZX?o@s26zbcBa4Be6Mq+;vnEEX*XQIpLag6 z7?L@>(E )+Oeo^A4LL(p{ Hac< zE;~?c&p_^}`pIc-E&n>zu({r63Wj8rLU`?X_szMoqXL{ae@Axr? zyuA_-5O5f51%8Kp_oMD1=*_L>TB$PbLHpbd!;19@vq^5&%i9~=XSQ}4&+YI=+E}h= z$e715Lp=RCKVJ$->ZuFNwN^w3YsNpp=msAo+24=< 9??kQ +b1S^oV;C7?+3qD(MVc{_X0{Uy$_{2_A@qFruPYPAHhcxwR za&q!SbJPL!RYP|eb ZoL8YTSS*6i-dzC9&}2+Q!=FqV z_w|{co`b#pG7 PWdH(cn1v7E9`OE1 z|2~`PpySd=6@+!@&V8CpYKsB%*e$N2LJs_IB>&<586K`U*Yfk(euM{ol;bj(s!?*= zTS6ys1{gT1WNNxX+y~P*FuQ|>r}9|<$4pNWPKO=^zQF-LJM^13sVhqx99QoKAIkMt zoxqXgLbjv}^c$@I;vyjR-`jNYU~b@)rIcbR!J_T6kPp;v-U-nXC0FJYwLBvqeJ?6e zB5#m_dh)2KAjRJB(F^Y0BGOo|k(3z9=SlKv^acB;xsxD~r++5eACZimTg5e-&KF zI2f3ppFQX~IM|{;7r3&Q#%vfh)HC6 XG-lmH(5 zYx33qz>n+&-6A3eESk9$<>jjw(r!o!j8@%_A7~C^G3*2+0hW%>pFLYthD9P?IATD6 zkN>Ek9C|XpmXuVE06YhKa|#LyXrzX|%MU?871NHYr>%baAQ0b(Ydx@hqcxCk*}W#M z5<{14#*g{_`0)d*QQ3wZb3i!csPN*&odFu7Kpd%ftJU}p+#*1;c~yZu!~U<^E|@&9 z`LqBA(+wYazWCr7oJ$}j0#gx)#cNqKsQyB;QSi6pu$lj)^b9%84n2B$dK2Jn;788l z9m!(n38n z$NA0 z{rmA82n3gm(Fx@0M8H*%$+~4=va|!hQFR9VG^?trP}R_Z@6;M6Rw5`T*A7jYHwYNj zOS)TH%r+N?LAq#^?E>7Is+yXbt*xzrfq}F0{^rssFsyacM#vJRzn>jC`dVm21Oy<{ zm3pC^B+-ux@PdwnaMxbBa~UlQ&KqBtRP!^NN@`oIT)=R4D&*ZXFp#mCH%N~lev0X+ zqN!p1wnwQBZjcJUe*GG>l4%kYz%_pFCAo a#j9%)dMz!+ z8-_LbHFF+`i^H~pHGbqGtEv_UovgFfXMJc-27@oh!JMfJ16cv|=o)q3$%0t|QrE); z@h~FIpi?AA*tRq`n?jyyXJpF;H3Gwl7a-v<6nR}q+xM~5FSq&(o z58&^{po3IO-$yO0a|5hkQ9#B3*1xN;`zv8^0oO+ZV)9zBqlFz_>tkYK^2KH$VWJ6K zOz-McFBazp8w7Te0X72X0WcN=dxXq_Az3QhIMi)rc(IX@5gMJe#TcOblo9sjt-E)# zU?rh@1zcH2Y{+4YY=9HNvKRtk8`yCVB_}U0{P5v2t &<$0p-o5K(nT%CX%0E0&;CF0Nq(dJ0J(|jqwpLs0qIL1iExL z(@LFooY#Bw9M2;Vk 5YOdwo0`#n5^v3JPV>`dA@Gem-T`2sL!I`64Uy-we2>~KOVuzb5d{aI#qELK+# zt?3J7ePv~(%T9sI%HpD^xcDdq`9)^+3iQhrBP(JB&ZKtx^6Hp6s%X&_O%@+t(Yf`Z z&Um9a)6esUnJJWPGXAN7T*WMGw5*=^%&e@|yPHdCPnDH%2C9FZ_KPRU(wrx;nCNI~ z0s>FCZgFQv+s=-iX$H(t#>ev^Nm7Vg%TO(7YHQ0;&e1gr>`L~}$jI>VsfXtG`X4`( z0!9avVsmp>!N)U^1sIzEa#S6s5jb%I0?)k<000iJmX(?ERbr`|HPhx@)q)lib^%n^ z2h1%C-d<*7zIxTIIYu=;CB^%;uyL`cJi{|I9r5+YwT+Fxa=?w6*M1ePr>7?(Qj?jP z31l~6eFt9So_@--bLOsjs(NT^&39nJos9*2VTnVUZ{4`@f!iU!tc=&`CAOv=Y9}tU z-FLNQkdB@+r4#2>714$+7FHT=^2p3L#fG&JMzzL0aqAdprHZK5?ez2UIYmSyB_Yw+ z+?;sruEqFc1sfZip&|=CF)>0X`&Q3;49sB8^6ljj@XGno)Z`3q7D@q~yxNssX=#i+ zb(f(j_v7{U54 HwQczf~%>ytyZF zlaX;3Ty3siz3QTz#)F=hdLqB!_RA3K_|$%BL|9Q#ar;I+t8%{4B?;Mp#&| ohS=wXzHQHb|ego1|AQ_Md%AB{~mO~Lb^UQZ8jKI`l2%drNRUvu+&h7)6B zmh=4|^WfG~NK@jc3Ns1>cGG7wx*59-$cRyLxf*CKWzL&7>zbRTrl}>ix3`z~b4jy! zoz7-jo0;9^X3k_4ZIDVEzf9iog)ZQA0Z*9e`=kmml$|R2#5@88qDqICY$zKKlXi$I z*jHe4K>@bmaM8ve7amW2J}DD8M2eaLOLEd6I%5+6o*{}##>HD;1g#X_iuX&0M_hQN zDtx;A{rmSofcg3bo6K%*+Lr2bhgL|=#6Qj3o=s- d%iB^NDGv_8$s#x{t-#JWnyEOI*s(G`-iFG7Di7;rKq0N5M&lSdY zYk-x?Y4x0jHCQ+A4ok=NihT)-A`07}XZp@N!Mney(-|G~(Cw5ep2queW%+?V+K!K1 zYM61!?0p6Qcojoc-a%$w9VfXi$zJykab=r4A|%Q2{(%AIaBCT^TJXrp;i$5+FHPKO z7#e~baWAbKmM1XqeCIg&=92N9p(`x5dIs2RSpt1owW7#<>btx$VWw!gg%BaK!EJP@ zd>EidI2YNE@rfuI2k}ZUMfU-ob2x6^dCtGBlqi)8&g^2Ryc-n7T`u30E`{q9DrOUK zowCg>jn?Qd*9Kf|Pw@Ul8VFVf`KCjWc~d$l5H>f &a9E*IvM!jWHL;~nn0Mb) z?@exsoHn2A4;haePZjAnDNn$VZExz;&_!L!96uwh$p1+^$dNh1dvbCT9BEqKoZnH> zbWZ%3Q+yCs;Azn4C~Gw>m#DH*>?OzMKkoaeISAIYuhfY%Z%WGGiLlz`*uNJy9{J-^ zd-QWaTc(30V#eeq$$0<83V&tl)coh_T7D^6Z52-&fa2sS4evM @~e4Ok3f2At?_AhrWm7cVa&sYuXjkFU81wRXwj JfKyND=Qd%-2Dt_?Z%2M_QngQ zDNpEmG-V=8RhoZgJ}Jp1exoO4#cpXLZXvtU5o_=|#f@|+Cc@95;&5XEV=vm}( ZyK6;s>c;|3*g }FVR=E9qpyvwt v&GWYHCC?@;TMoW$ZQ90l?0$y?Jh zc>*2f-nKG%!u7&SRyUvWoH8XZ1Xw}Nf^$tFC8cW&Oj?(So35Z|F(nEQX| UV$*`SjFwJx6=Vh)#^i+kGGOwhQq0_h@(LpB!R8LE(LXEvuJVJQ&ss-wIk z_edP9sWQ(>YaE>YGq|%ql>hg>WOc_EXx=E>U(2{@f}1R`(~+vUrMB%OH02R rdDILEyvZ;J5JF*g*4@iLR@iJ2&V-ESjeHki`!K;T4~s% zzi@bR@`j?}NO+LCk9XR9+4x-fL`qW*XaFQxu0FjGFg-3akOZbDF;ceMyNP<@`_E I3R9?;2&(y`@s-jRSt&zb%nsEjy8gHW4p2hGn*x zU{d>0+;*9q@ ?*qn_xxok3X`{rn;~D zHl<9CWCH5at((yGV8A0Dr`*M;5Wc^5Y_1z{^5i1)DITmBlySqb2Xp!=AGVI<*od*) zgGp`@aM3KaN?NVUg$g$3!|*G(>!s!7g2TcdQEX<1Wl+j9$Wf$;t{8+#JrZv0im$7y z1G!$}%zI-2WY}1gD_qL3_6bkq6cip3usr`k!ohB&keAmemLV*xu%WTC{?kH!Liw*~ zIZJXFE_d2;^qONu{AG6l?IhAaj5HETi3pZ_J-r`yV+g8p_Db>V1XPZEmwrjPY!ipa ztB^*?!F^P(ryd47xJjp_Kg6;x>2pLbVQOgh{8{p7{EnfM`m$>#V$Wt&siqg9kih{? z-J6?O5!M5(oM}_)sQv;|s;U>$&CZdD)3-_Ijsf~!4SiP@(W#cHBGbiqZ 5wI5y^BL$_&_lI&L|Kq0V79wmYUnQM~JA29M@2goocszxpFIgir5Xxgu zG7XIdbO^bbSCql`$=}Z}LB^YMe##w^$%x1cTWTHSN`m%Gx3Y0J3PDsVM{~-PM2?QI zF5zd1g?1>@b};(MTtM8lTmd+}C?=T~)S4_k5Cv9ZM&Jpwqfh-*#9(FSf4ee2#e}Ga zRyUMC3sAKG$~ezVfRZmPFkXxE#J>}K>Ao)TI~t{q>|9*CzHEc<^aLr|X;DAT`JTLX z_|kcn{3l_y Y*9{kMWjt_|g_BiGU{L7Lb4gW+u zg@@sc-mg{5={|<)7(I%}JdHUr?K%D+MLAdgYg*;4@O H{e>K;2QxD>!=p(Q zR(hJ34t7Mxj~~xEV44qMQr&!{6kAfl#md@QUtbSi{tETpw2e;K{*Z{5R>YUXY8XZ$ z7#J8pnKU^!H#as$LQMQbPOfs?n<4bNym(e5r0XlItIx_(AFdcd>iO^laxn-js3>?G zA+!}nN59111oV={;rr~Wh~Hyz@z^Y5;77Eyw9 Jq9SFl}d@t81ekb=KV |5lHT1Hxv*YRrhrbUu2c{%^ya)2*x3H1uVG ~qqv6eNjRP|5YlQiP^_R*n-bSExsUEMYpD zU0*9XCFRzj#9!ZLkr?<+u$LSE^-Eq(Zfs&=#Y~cAU&dvBW-ur)FwW#Osa+I=oMkp- zit%04i+QlbhKDS%T!gaj9FcG{P@Q$a`_wVo!3ft73lmfNcmtK#;iJ_z5$Cv!K=_{% zz}t2Ah==GM8WI8$<+rd4r>V&=U%q_lQe9a_H2Y7PfQ?IBwu>cpE4a-CE?n4AD@ehR zK~T7Jq)W>Jcda9M_*hz2r$qidCP;NC(J6+P$$_DjtdX3#$;`|Hq%1H_%1>2_P{}Hf zdPBy0l2~W(5m-&!0>TXvORwqR;IJNRnd)`}Spii#D{JfZwKXUx<&BH#>TP!s19#i* z>tUzj-XY%ys6!Eu7sZt;Ftr4xH#ES`fhvjfM1&2vsp}t$XUdMLZF-z%uL^Cq5cfud z#*vL;$x6z~Vl$^toQQThbLPxojvi5}_~Al`^|f+c?$8}TAjnVuUvdy`7*Z0FPaIa$ zhCWGnRzac15QtH`UCeOS!NMHn&Ka(UBqy@7vu`ml{JO*&tK9BdTz4P%NZV-=^6BS= z>7no2t8|83YbIwh$`t7EQ}lFo73Ack-HPjSj=-x{J@CJT*?yfzbtNdL-ERFsHI*Ov zf~=KKK;S!oM|j^9CTn0&Ml)$pi`ILsYB{=DrPQbwDX4dmk& 2?U z3R8X3nD@!6p+1pi)l0~DyGeJ+Iw SE9q)voKG2+szJ@lbC7I&8GYKZ@Prx~c(??zpV| !Z}8>H+oH6< ziSy-2U|b7We83|yw(by~`j+zQy`=)((WeeDww0Ha{odQ2S*$CWCy;zUDh~NRBqTe( zkVf96u`a +`M5aXq2S2lGq=n2cwscxT5C~Q!HyI|AV_ZnM6Bo9W!REG69mm=) z#@YilEZ6%F#6ooPBv`d3tV4{uN=ti#3OfdZBo890rVwvmU*ETH8&H@tWSxvM>C!CI z%^&Z30*K77oq@cCE6-~Wmc=Ia=DD_Sz@`hn0r o?|seBb+Ra6UMdGzR!y3?9GJv}`r z=X)6-NGSOG_!xG5rdUW&Q8Qo@(>Qftk?8I14OQWFaJk-}kc5LZiA4GiOtmH6 ^79%V;+sw!8w&dZH;fHpy*kEPQ!`qkLP=IO zFe*yJiaT8*XsQc+kOT}LC_t~UnW(`aq<%5!4W@gJUvgUjK8=l!|KK( 7Cdl4W3j6(wm&~vH)&@8-Klr82_Xy!EB`2Wq$>Tp41tA z KwV=nDGyk==T>d+1<)gJq)cC(N7;=E6-qii}iOQrd)C z*29Mnvz6EK3docOkCnX&W4wI|f$)t$VupbaiceHmCqyD~GvKJ`E2}{x1~#ALKY#K$ z;asFIYz;tJAhc}v^;%X5YwzlZyE>CHU;c%8Nc9W1Cu99LX+bFF0*v0~wX43WfM#+p zw{HT+{soM;)f1Pxfryv q7w80t+r1~g1?rW0BB z#Eeri_dh+@1qu~cv;~qUu@0gf)>cZGb>ltQbuWC~sN8@2@9HhVcdY08m1Skk;h97B z_QvPVh&`m7y`@eLoeJsiw*4>OrDb67xm{%i3@aU-@aM3kru6YC?Y;PPC5kR4%d;8L z*H6_SMeq@83ZkQVyV8{JJ8r%(0Gn=HEww8i-?uYOqT-?hmL7DcNJ7GMoT^IUI7%h* zy>d9^$lu(>7u_1Fo(`er4Zu)F3Q#@j7JmgSsO5C~n|0r{3m1q^oDh|i)HPOdff%ja z_q$jCgB!{$)8AxxKAbeWZX}y0Fzo^p{W?Q(jd}uG->9) >U|Y;+BrME9L%Vd+ zAUl&0fj}L}f9=uQDr-LuL%JAX@zlGFM1!Ch^O4i$vr=wmzPr0SI9#^8I~c7s)$588 zKBYj9i@85QxML@L&14U7LavMoQTQ&@Gm5R}RAglMuX4~KR`%UJwDN^ 0>|1MKGUIl?h;_IsdPvg!&au zV#9qh$7V-+poB((2ZlIB&(v$)y8p{io!Es-6JYwhh wP_f9XKLr*kn>g`E`vlm1quwr474g0P|P0a?;pFQ+xJV3L2fMrK$aHN zjm %S|=F*4Gs^GlG09EaTF+c=i`E>#qYfR(%sh8k9sz0$NYM%@9l6z*Yd zKTdgH_jKw7Q0L6{tbkIxeIM%lP_pm#zbG^kW?^9gl3p0RX!jiCs_nx#y27@qqhk#& z6-RRrjKIw-hhd3&VD@Tn)%`7OxWm$syMP-w*e*0D`-$cW$R8NANl8sE;_CGQ0WuIQ z3d;88iY6j;a!}32c_Wr@C?2eM3w7`pzY3GXZO1k&A4Utd3fLORwp__rfDjhh0*OdZ zD4~+mcXz_sRHDiW0W_V;!7AZM@;R38FXXyaYM!jl( z-6x9xjg*|=2hf+2Kuh-?Wd18v>>cNl6_Tc#rE9^Emt^ikY^)9linoY_;^G;jn6vQo z)Q6qf&91kqO`Uvn7}6x%2C>p2;6sbiGb!r*369;@_bXtU7-7*X2 oN#!X=b1IRyU7 zE8$Ok7yf<9`QQ3cvL{wkPmey7(iguEemmkrYBP2*iNH#=%hb>X{+#(OAG?^yyO#{& za}bD?Dj8*FDO(6abOi{ozOiBR;>F_Xsv}tB`crUTw>e5N>wLW#{@xv^Z37DeB?KtV z1_Er#VC%jUTZDl@o^?U=8ig+~xw$ANrnw;Kx*{YD;B++!qrU{AfZhZw;Fm$DXBj|! zN)Oh$#AJVzkh~1oBpgAuqz&p4ZO{J&RZ5 ?txvoOk#dWU|na-`?I1gA{>rZ~nv{)B!RWDsXiLEE*7S z_r*`X#-+EcBoq`*v)x%EBii~Qb_W1wfIbM$lfRtase*m*)^Qer(EK=veHR!wGCi## zD=T2U9n_2J&sHACly`M)KvPbd6Ih#P3F1&?+r@>SKfm>YUJZz+w1}XfHmGcMuCJ`b zRw?fQwhkLrhAbMYwQzU=H467dfCkKEiF%FKc2N`fM<^Kg#2LimC)x!9OOdkXiBJA^ z&cJ>qy`Y E)Fj&M1_P@Go<*EfBF@48ZdhT11eMT)QA^DsqN2-)22$cZQsaSpTUW$dT-(E z> W*NGnlphv)QzC4Z(QfsQKTSuUD$8W)Pijc4eCQ82k z0C^5PACQ=16M7g2c~Pj~xfId77K5BU6v ;J}_vS7KX~A^4O<)V3MstI#ya% zcKL70j8QeeVQ^3#ieU;03ZIp-XsM~cm6yBb=jk<5DqJ`qt%ebtEHychjEIPc*gW;8 z^F<)Y&WQ=}%K;Y(Sx#;dT Jt(R={Y`K<$f9L+=?Awmj3 z6%qaBZ@vls^k2n=x08?&AXGDl@i66HlgkIVzeptDhM|E3 QF*Vf#;DWVoroE=C3uLv=L015p zi?rG-lRwzZ7ymalBduEFd+LRufto1?M;Nn6IIHpY+KkOu?KvI>78WbmIDXgLMPbNO z&ZNNVWXmWp?j-_qkXt@^s^>|SmhJ+z@{th`2z8RD13D^dx{kA@&;JfQNy+jL21{LN z%m`&vrfuH;Sg1&F<_h$TNCBM%S_P>{uA8ez5$H #H54F!QC<(CmvWwp51sV5ee0Gz;1kfH+}gNvZo>eyI;#cafD=Fs1RBh(cw*Rq zx)$ggkSY86&H?BLeGv>o&}$5<(%=G#;x9OWCG>((agYg-(c9t1_u`U9;FjpEG{F-N z3d#Y%ijiST7qK`!9q7b0O70iJG+<@>5@_!;0~MQL+uAmO2T18_CUB|8z!$~S$PiAY zr@Y*g1jmkHxxaC?E21a+3Kk$kQ4S{`k@W5@FqL}ans<|J70&4RZWMZ+G(HOw`FeT8 z%j&vu>^FG@g;q@|Hzapp_Q@)*z`($;ug<5Btd8B1T*hB`j8svW{&wy9b-yd@tq?1I zS7JvPlQ=2KVFLmdzBPF1O5=~Zx^oPusrmV)UdykPT((#U(vUN^jKOFV3F8zmOgY#g z&m5HJ%882B!WX+uHuD*hkHb*Oh6xIK(zwNo!SY!shqt<=gm0<0xMIs;YduN9L!e}# zF5CcA+!YE6Bo%C$9-V^etW0$1axKMy^m*KgL zM3m>DTLi+aHs+sBH}@LF;dEjEzmoY0%|Yy6@;T8haxKVa&`uA)y=;~rjInNVJHdjM zGH5!equO8egq1Jqc9*?3#6~E5HhCHJUu{kdpr9hZ4H_fvMdQH|b~?Iv5Iw-Dfo=)d z(ONlVx7E2czahftoDyAcGf2vUL@E%VBTQ?7L!x7aW9hEfLaWnxV&W-we;&uFsi3a- z4uQEzyEZIL97r`iTDmjO2}WzMe}J$Vx8kN)O^fO;bHTNyTHf~(4v09@N}Ixlls$mt z=83`!YwLuZTsSzTrh%Y&N){kgN&yUS!0PGoC!qcgqCn)qF>Hp|ku#a|o{|6z!>68D zLEBgwI`Iq=c4`a@R_`v|>Frrv+_em><&p{LH>U@_6aZ21SKW=NsZZJv)Jl^LJ~sGl zTHlYYEL_0`u^FxeSb-*Q^~C-J?}2iP>RhzN-ZdaSVO|QHMeQtROXoy>7#LvBp=eHu z_;wMF|Jw1`{UE;GE@G0kk k#zyHrM$W?-AoQ= Nc2) ~RCB-` zWPe(*48&uggOw8yI2v^dflZ=8T@35M!j^V}HAzz~@Y|2eM=7d2x+0AWq~iKb>6{(v zOA^vqnVF0JF1tFop-9k5#XxdZ;j`Mu&`=PV3gg*9NiesacYnzrm_eXitft$i;2LUG zd!zd`I{F(-)5s5xjKtv`2s7~1X10EP1hV5OT(-(cBs`}1wnQQp&~I+Vvg-j(mXkXM z@vmE-r(j;rfzCK4JG&J?Mjl|BO_(z5W5K>cp$a3Va4|Hz`8jF^vWt%(QBpgOK;MGL z(`bOc2pU4TNt0nXUOuqxq83c<_}eQ$4xb?;+=BOrFS1JPWhi|EV#j{Sj6qC>jpk1q z86UR>05 a{a>c#Tvo(hFeqwhfV>{5pyb? zL5@Y(3=T^ThJ_~`KjrXTF$mPjfRMvGpf4`p3qEpew2rB1b8jn$on9lY@h|x6X-w i3r7OCL*gPzv5Qa%NlLnrti%c5q2J}gg;2O5LC&>kf_nEK!^4c$UO0>4_~hh8 z5)u>)PJ^_gG`LXGqsxv;MqPan#FRHUy8HTeHaCStM1YD>nLmcWK7d0(PK48BE8zYg zuAngqX(0^m1t>`MH$sFZ_P}YixBpLHq|$Z*2|80A>)8)#^0E5|$ I7` 2Qb^hk_Ef7*YET#h z6#@%A_Fl!u+Z&g r@?f~$)TFKF(C#=JaAlTORuNow_txr~ znm!ku*>bu1)c+vW2=S7h48GbDR$5vb269w5x3$7^f~T{wFa&M$ysV-AuV)MR*5>*Y zQ^eCcfqYYiuHL6#tUEqwh?5BjLN$xRPi}1QV8i7g)OrezTEaRYlPYW`gf%#Y+(7HV z-fJT!hn@;t_eF;*-~l9j=W4+Ucn;&uQMU(V8))kvR|L8obR_<8|JJ?ej%~S30vn z0Bs9mSLhjFB%R?#Nw=OneHtX^W5Q 5kDiWZo;lu1`9EZcJlS598ay;1E05l09=B!VN8BA^i z79vUV1cGn$6+HRhI0R1YZhq#@N0itNb+t0*fs!Vr^fr$hY?*c20y>V6npZ6_k&%#O zDwB`{bZ{Kjo4q(SxF2<}6-8y>;Q#7qs(hV@jXblR S$JDa2ajWeI% zUWQ86I_&mz_OcL4MKrEM9<~MpsU2uPC?VH)Hw||L`Qe^ibekgm{=Xd7u*SgZl8n;r z6o6A{%)@)~R;7W^MADMa1Fb*m$$2%T nTi{#%@;8y@SF9Vt8 zMf}49-8}|<>{qyKGvN}sbNcWeZ%~O7O@Si%udmFaEbr`~O_r_<#4E)*<4)Wa?gY z{SQ4c3Wp_TMB-^&h(?U#pcjHKRO9f!zI@K>;X!r4eI5SWOBU hFrw2#5&P&`}v!S-}yNlx%`dy;A*KlS-3blZ9h~#}S6`L5v=b zrfwDX9ovr`4J67dhNHvD!m 4JZBm zqrBp<$oeu!8|+*k`uTbJiSx)IGVw7-zUj^usZn~7vs+6qV-yr!pxR{Kb=~zJKR B9G8!2Vbq7o7mtVSPV30Yu* z`X-~9P+YO6=V4<{yn4G18hA-BKR-}8gLp;)=D|VANV{Qs>|gI7j0Sg+dFT!S9q+$V zavoaeow)*BKZW(&_VYS>Sq;oEpv7FzV0{3dDJdy%lR*=}n4~}Mv*H1w&g_XjfducM zWth2D`2)(aV6LJR?&}2wBao`|!d=;&UJ7~e2bh+)vru+hQ3B#SPVNb}K4dn&6rB0F zxxpP%lW?8^1^5H%1Mm@_kdVE7us3u7P1?16ec^ugAi+)B1eklU+k60HBS0PDyw;(3 zu;lNu_&(D^5;jQQODsY&N8}v& R#9ZC;|r zKakp<+wvM1*YzhHXC;1L6KN76B3Uo
RufIC4qkB-iXibN|Uwt zQh^F+!h}g{V<~US$_^5vob91J3RTl6z&nl~6kmHl)hw(`A0}{?IPVq9?Uoz;$2SLm zGC(T6{{9mO_OGr(TX44;4{fV-7wGSx=)0f&j5|34`{LIRQrITF2f(1hozyY{6w&jX zBxkt5p{g_wY~<~d#aA8JOyF$b8B8-eCk{j6={R3B0k-|PT0d}lux-S}r=TTI`V?#f zRtFM{t6H(;o9Zr&a3Nvgr(nj^d|pIox)Z8TL_C`>6{m%rUgHZ~-3I6p@#z!Ph}z0r z4q)qLgNc~PQ9L}Ty?p)p6&|l4%OS1+ihL1v|KcK!$7SHLU3?ib2ip|VGC=`>Gn8C6 znBs~5I=QpwUVGG}QVI&j!fyQid4M@Q3fscq2rdfz$5W0WgcJ1pd`s-7eW>ojMHAP5 zMw4wIs)n34B1-tKiX4WoTL>jL+&)j&yVY%BN4($S^2Zjo!ye;i~(`5ycg@6tbiPDuf(2s5#48g`O{jDwbgc}x1pH}d~&5$4yy5fWE| VnV}YGd0cE36P6VEETH-7{{sj_a3uf$ literal 0 HcmV?d00001 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsDialog.java index 2a5bc5a226..63d7589076 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsDialog.java @@ -58,6 +58,7 @@ import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.util.ProgramLocation; import ghidra.util.Msg; import ghidra.util.Swing; +import ghidra.util.charset.CharsetInfoManager; import ghidra.util.exception.CancelledException; import ghidra.util.layout.PairLayout; import ghidra.util.table.GhidraTable; @@ -69,10 +70,10 @@ public class EncodedStringsDialog extends DialogComponentProvider { private static final Map CHARSET_TO_DT_MAP = Map.ofEntries( // charsets not in this map will use StringDataType and will // set the charset setting at the memory location of the string to be created - Map.entry(CharsetInfo.USASCII, StringDataType.dataType), - Map.entry(CharsetInfo.UTF8, StringUTF8DataType.dataType), - Map.entry(CharsetInfo.UTF16, UnicodeDataType.dataType), - Map.entry(CharsetInfo.UTF32, Unicode32DataType.dataType)); + Map.entry(CharsetInfoManager.USASCII, StringDataType.dataType), + Map.entry(CharsetInfoManager.UTF8, StringUTF8DataType.dataType), + Map.entry(CharsetInfoManager.UTF16, UnicodeDataType.dataType), + Map.entry(CharsetInfoManager.UTF32, Unicode32DataType.dataType)); private static final String BUTTON_FONT_ID = "font.plugin.strings.buttons"; @@ -443,7 +444,7 @@ public class EncodedStringsDialog extends DialogComponentProvider { private void buildCharsetPickerComponents() { charsetComboBox = new GhidraComboBox<>(); charsetComboBox.getAccessibleContext().setAccessibleName("Charset Checkboxes"); - for (String charsetName : CharsetInfo.getInstance().getCharsetNames()) { + for (String charsetName : CharsetInfoManager.getInstance().getCharsetNames()) { charsetComboBox.addToModel(charsetName); } charsetComboBox.setSelectedItem(getDefault(EncodedStringsPlugin.CHARSET_OPTIONNAME, @@ -908,7 +909,7 @@ public class EncodedStringsDialog extends DialogComponentProvider { private void updateOptions() { String charsetName = charsetComboBox.getSelectedItem().toString(); if (!charsetExists(charsetName)) { - charsetName = CharsetInfo.USASCII; + charsetName = CharsetInfoManager.USASCII; } boolean scriptOptions = showScriptOptionsButton.isSelected(); @@ -930,7 +931,7 @@ public class EncodedStringsDialog extends DialogComponentProvider { settings = new SettingsImpl(); CharsetSettingsDefinition.CHARSET.setCharset(settings, charsetName); } - int charSize = CharsetInfo.getInstance().getCharsetCharSize(charsetName); + int charSize = CharsetInfoManager.getInstance().getCharsetCharSize(charsetName); updateTrigramStringValidator(stringModelFilenameComboBox.getText()); boolean requireValidStrings = requireValidStringCB.isSelected(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsPlugin.java index 9e60ce54f4..9fcb88bcf7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/strings/EncodedStringsPlugin.java @@ -30,9 +30,9 @@ import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.data.CharsetInfo; import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; +import ghidra.util.charset.CharsetInfoManager; import ghidra.util.datastruct.WeakDataStructureFactory; import ghidra.util.datastruct.WeakSet; @@ -52,7 +52,7 @@ public class EncodedStringsPlugin extends ProgramPlugin { private static final String ACTIONNAME = "Search For Encoded Strings"; static final String STRINGS_OPTION_NAME = "Strings"; static final String CHARSET_OPTIONNAME = "Default Charset"; - static final String CHARSET_DEFAULT_VALUE = CharsetInfo.USASCII; + static final String CHARSET_DEFAULT_VALUE = CharsetInfoManager.USASCII; static final String TRANSLATE_SERVICE_OPTIONNAME = "Default Translation Service Name"; static final String STRINGMODEL_FILENAME_OPTIONNAME = "Default String Model Filename"; static final String STRINGMODEL_FILENAME_DEFAULT = "stringngrams/StringModel.sng"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetInfoPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetInfoPanel.java new file mode 100644 index 0000000000..b9c4553649 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetInfoPanel.java @@ -0,0 +1,161 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.charset.picker; + +import java.awt.Font; +import java.lang.Character.UnicodeScript; +import java.util.*; + +import javax.swing.*; + +import docking.widgets.checkbox.GCheckBox; +import docking.widgets.label.GLabel; +import docking.widgets.list.GListCellRenderer; +import generic.theme.Gui; +import ghidra.app.plugin.core.strings.CharacterScriptUtils; +import ghidra.util.charset.CharsetInfo; +import ghidra.util.layout.VariableHeightPairLayout; + +/** + * A JPanel that displays the details about a {@link CharsetInfo} object. + */ +public class CharsetInfoPanel extends JPanel { + private Map scriptExampleStrings = new HashMap<>(); + private JTextField nameTF; + private JTextArea commentTA; + private GCheckBox fixedCB; + private JTextField minmaxTF; + private JTextField alignTF; + private JList scriptsList; + + public CharsetInfoPanel() { + super(new VariableHeightPairLayout()); + + build(); + } + + private void build() { + + nameTF = new JTextField(); + nameTF.setEditable(false); + nameTF.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + add(newLabel("Name:", "Charset name", nameTF, false)); + add(nameTF); + + commentTA = new JTextArea(2, 100); + commentTA.setEditable(false); + commentTA.setLineWrap(true); + commentTA.setWrapStyleWord(true); + Gui.registerFont(commentTA, "font.textarea.astextfield"); + + add(newLabel("Description:", "Charset description", commentTA, true)); + add(commentTA); + + fixedCB = new GCheckBox(); + fixedCB.setEnabled(false); + + add(newLabel("Fixed Length:", "Charset uses a fixed number of bytes to produce a character", + fixedCB, false)); + add(fixedCB); + + minmaxTF = new JTextField(); + minmaxTF.setEditable(false); + minmaxTF.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + add(newLabel("Min/Max Bytes Per Char:", + "Number of bytes a charset needs to produce a character", minmaxTF, false)); + add(minmaxTF); + + alignTF = new JTextField(); + alignTF.setEditable(false); + alignTF.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + add(newLabel("Aligned Size:", "Byte offset that is valid to start a character", alignTF, + false)); + add(alignTF); + + scriptsList = new JList<>(List.of().toArray(UnicodeScript[]::new)); + scriptsList.setCellRenderer( + GListCellRenderer.createDefaultTextRenderer(this::getScriptCellRendererText)); + scriptsList.setVisibleRowCount(5); + JScrollPane scriptsSP = new JScrollPane(); + scriptsSP.setFocusable(false); + scriptsSP.getVerticalScrollBar().setFocusable(false); + scriptsSP.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scriptsSP.getViewport().add(scriptsList); + + add(newLabel("Scripts:", "The scripts that this charset produce", scriptsSP, true)); + add(scriptsSP); + } + + private GLabel newLabel(String text, String tooltip, JComponent comp, boolean top) { + GLabel label = new GLabel(text); + if (top) { + label.setVerticalAlignment(SwingConstants.TOP); + } + label.setToolTipText(tooltip); + label.setLabelFor(comp); + return label; + } + + @Override + public void setFont(Font font) { + super.setFont(font); + if (scriptExampleStrings != null) { + scriptExampleStrings.clear(); + } + } + + public void setCharset(CharsetInfo csi) { + nameTF.setText(csi.getName()); + nameTF.setCaretPosition(0); + commentTA.setText(csi.getComment()); + commentTA.setCaretPosition(0); + fixedCB.setSelected(csi.hasFixedLengthChars()); + + String min = + csi.getMinBytesPerChar() > 0 ? Integer.toString(csi.getMinBytesPerChar()) : "unknown"; + String max = + csi.getMaxBytesPerChar() > 0 ? Integer.toString(csi.getMaxBytesPerChar()) : "unknown"; + minmaxTF.setText("%s / %s".formatted(min, max)); + minmaxTF.setCaretPosition(0); + + alignTF.setText("%d".formatted(csi.getAlignment())); + alignTF.setCaretPosition(0); + + scriptsList.setListData(List.copyOf(csi.getScripts()).toArray(UnicodeScript[]::new)); + } + + private String getScriptCellRendererText(UnicodeScript script) { + buildScriptExamplesMap(getFont()); + if (script == null) { + return ""; + } + String name = script.name(); + String example = scriptExampleStrings.getOrDefault(script, ""); + if (!example.isEmpty()) { + example = " \u2014 " + example; + } + return name + example; + } + + private void buildScriptExamplesMap(Font f) { + if (scriptExampleStrings.isEmpty()) { + scriptExampleStrings.putAll(CharacterScriptUtils.getDisplayableScriptExamples(f, 7)); + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerDialog.java new file mode 100644 index 0000000000..24d6a139c9 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerDialog.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.charset.picker; + +import docking.DialogComponentProvider; +import docking.DockingWindowManager; +import ghidra.util.HelpLocation; +import ghidra.util.charset.CharsetInfo; + +/** + * Dialog that displays a charset picker table and lets the user press ok or cancel. + * + * Call {@link #getSelectedCharset()} after the dialog closes to get the selected value. + */ +public class CharsetPickerDialog extends DialogComponentProvider { + + /** + * Allows user to pick a charset from a table in a modal dialog. + * + * @param defaultCSI default charset to initially select in the table + * @return selected charset, or null if canceled + */ + public static CharsetInfo pickCharset(CharsetInfo defaultCSI) { + CharsetPickerDialog dlg = new CharsetPickerDialog(); + dlg.setSelectedCharset(defaultCSI); + DockingWindowManager.showDialog(dlg); + return dlg.getSelectedCharset(); + } + + private CharsetPickerPanel panel; + private CharsetInfo csi; + + public CharsetPickerDialog() { + super("Pick Charset", true, false, true, false); + + panel = new CharsetPickerPanel(null); + addWorkPanel(panel); + addOKButton(); + addCancelButton(); + setDefaultSize(800, 800); + setRememberLocation(false); + setHelpLocation(new HelpLocation("Charsets", "CharsetPicker")); + } + + @Override + protected void okCallback() { + this.csi = panel.getSelectedCharset(); + close(); + } + + @Override + protected void cancelCallback() { + this.csi = null; + super.cancelCallback(); + } + + public void setSelectedCharset(CharsetInfo csi) { + panel.setSelectedCharset(csi); + } + + public CharsetInfo getSelectedCharset() { + return csi; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerPanel.java new file mode 100644 index 0000000000..5626cb3836 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetPickerPanel.java @@ -0,0 +1,103 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.charset.picker; + +import java.awt.BorderLayout; +import java.nio.charset.Charset; +import java.util.function.Consumer; + +import javax.swing.*; +import javax.swing.table.TableColumn; + +import ghidra.util.charset.CharsetInfo; +import ghidra.util.table.GhidraTable; +import ghidra.util.table.GhidraTableFilterPanel; + +/** + * JPanel that displays a table of all charsets on top and a detail panel on bottom. + */ +public class CharsetPickerPanel extends JPanel { + + private GhidraTable table; + private CharsetTableModel tableModel = new CharsetTableModel(); + private GhidraTableFilterPanel
tableFilterPanel; + private Consumer charsetListener; + private CharsetInfo selectedCSI; + + public CharsetPickerPanel(Consumer charsetListener) { + super(new BorderLayout()); + build(); + this.charsetListener = charsetListener; + } + + private void build() { + + table = new GhidraTable(tableModel); + table.setVisibleRowCount(10); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + tableFilterPanel = new GhidraTableFilterPanel<>(table, tableModel); + + JScrollPane scrollPane = new JScrollPane(table); + + CharsetInfoPanel detailsPanel = new CharsetInfoPanel(); + detailsPanel.getAccessibleContext().setAccessibleName("Details"); + detailsPanel.setBorder(BorderFactory.createTitledBorder("Details")); + + JPanel innerPanel = new JPanel(new BorderLayout()); + innerPanel.add(scrollPane, BorderLayout.CENTER); + innerPanel.add(tableFilterPanel, BorderLayout.SOUTH); + innerPanel.getAccessibleContext().setAccessibleName("Table Filter"); + + add(innerPanel, BorderLayout.CENTER); + add(detailsPanel, BorderLayout.SOUTH); + + table.getSelectionModel().addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + CharsetTableRow row = tableFilterPanel.getSelectedItem(); + if (row != null) { + selectedCSI = row.csi(); + detailsPanel.setCharset(row.csi()); + if (charsetListener != null) { + charsetListener.accept(row.csi().getCharset()); + } + } + } + }); + TableColumn col = table.getColumnModel().getColumn(CharsetTableModel.MINLEN_COL); + col.setMaxWidth(100); + col = table.getColumnModel().getColumn(CharsetTableModel.MAXLEN_COL); + col.setMaxWidth(100); + col = table.getColumnModel().getColumn(CharsetTableModel.FIXEDLEN_COL); + col.setMaxWidth(100); + } + + public void setSelectedCharset(CharsetInfo csi) { + int rowNum = tableModel.findCharset(csi); + if (rowNum >= 0) { + table.getSelectionManager().setSelectionInterval(rowNum, rowNum); + table.scrollToSelectedRow(); + } + } + + public void setCharsetListener(Consumer charsetListener) { + this.charsetListener = charsetListener; + } + + public CharsetInfo getSelectedCharset() { + return selectedCSI; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableModel.java new file mode 100644 index 0000000000..236e70a116 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableModel.java @@ -0,0 +1,125 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.charset.picker; + +import java.lang.Character.UnicodeScript; +import java.util.*; + +import docking.widgets.table.AbstractSortedTableModel; +import ghidra.util.charset.CharsetInfo; +import ghidra.util.charset.CharsetInfoManager; + +class CharsetTableModel extends AbstractSortedTableModel { + + final static int NAME_COL = 0; + final static int COMMENT_COL = 1; + final static int FIXEDLEN_COL = 2; + final static int MINLEN_COL = 3; + final static int MAXLEN_COL = 4; + final static int SCRIPTS_COL = 5; + + final static String[] COL_NAMES = + new String[] { "Name", "Description", "Fixed Length", "Min BPC", "Max BPC", "Scripts" }; + + private List charsets = new ArrayList<>(); + + public CharsetTableModel() { + CharsetInfoManager.getInstance() + .getCharsets() + .stream() + .map(csi -> new CharsetTableRow(csi, getScriptsString(csi.getScripts()))) + .forEach(charsets::add); + } + + private static String getScriptsString(Set scripts) { + StringBuilder sb = new StringBuilder(); + for (UnicodeScript script : scripts) { + if (!sb.isEmpty()) { + sb.append(", "); + } + sb.append(script.name()); + } + return sb.toString(); + } + + public int findCharset(CharsetInfo csi) { + for (int i = 0; i < charsets.size(); i++) { + CharsetTableRow row = charsets.get(i); + if (row.csi().getName().equals(csi.getName())) { + return i; + } + } + return -1; + } + + @Override + public String getName() { + return "Charsets"; + } + + @Override + public int getColumnCount() { + return COL_NAMES.length; + } + + @Override + public String getColumnName(int column) { + return 0 <= column && column < COL_NAMES.length ? COL_NAMES[column] : "< >"; + } + + @Override + public Class> getColumnClass(int columnIndex) { + switch (columnIndex) { + case NAME_COL: + return String.class; + case COMMENT_COL: + return String.class; + case FIXEDLEN_COL: + return Boolean.class; + case MINLEN_COL: + return Integer.class; + case MAXLEN_COL: + return Integer.class; + case SCRIPTS_COL: + return String.class; + } + return Object.class; + } + + @Override + public boolean isSortable(int columnIndex) { + return true; + } + + @Override + public List getModelData() { + return charsets; + } + + @Override + public Object getColumnValueForRow(CharsetTableRow row, int column) { + return switch (column) { + case NAME_COL -> row.csi().getName(); + case COMMENT_COL -> row.csi().getComment(); + case FIXEDLEN_COL -> row.csi().hasFixedLengthChars(); + case MINLEN_COL -> row.csi().getMinBytesPerChar(); + case MAXLEN_COL -> row.csi().getMaxBytesPerChar(); + case SCRIPTS_COL -> row.scripts(); + default -> "???"; + }; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableRow.java b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableRow.java new file mode 100644 index 0000000000..37e2b78ad5 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/charset/picker/CharsetTableRow.java @@ -0,0 +1,20 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.util.charset.picker; + +import ghidra.util.charset.CharsetInfo; + +record CharsetTableRow(CharsetInfo csi, String scripts) {} diff --git a/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm b/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm index be229f4040..d71cc211cd 100644 --- a/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm +++ b/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm @@ -7,213 +7,214 @@ - The Byte Viewer
+The Byte Viewer
The Byte Viewer displays bytes in memory in various formats, e.g., - Hex, Ascii, Octal, etc. The figure below shows the Byte Viewer + Hex, Characters, Octal, etc. The figure below shows the Byte Viewer plugin in a separate window from the default - tool, the Code Browser.
+ tool+
-
To show the Byte Viewer, select the icon,
-, - on the Code Browser toolbar, OR, choose the Window
Bytes: ... menu.
The following paragraphs describe the Byte Viewer.
+To show the Byte Viewer, select the icon,
+ +, + on the Code Browser toolbar, OR, choose the Window +
Bytes: ... menu.
The following paragraphs describe the Byte Viewer.
Data Formats
-This section describes the formats that Ghidra provides by - default. Each format is an instance of a DataFormatModel interface, - so any - new formats that you provide will automatically show up in the Byte Viewer Options dialog that - lists the data formats that - may be added to your view. To add or remove a data format view - from the tool, press the
+ default. Each format is an instance of a DataFormatModel interface, + so any new formats that you provide + will automatically show up in the Byte Viewer Options dialog that + lists the data formats that may be added to your view. + +- icon to bring up the Byte Viewer Options dialog. - Select the formats that you want and press the OK button.
-To add or remove a data format view from the tool, press the +
+icon to bring up the + Byte Viewer Options dialog. Select the formats that you want and press the + OK button.
Hex
---The Hex view shows each byte as a two character hex value. Hex +
+- -The Hex view shows each byte as a two character hex value. Change the group size for the Hex format to show - the bytes grouped in that size. When you add the Byte Viewer + the bytes grouped in that size. When you add the Byte Viewer plugin to a tool and then open a program, the Hex view is automatically - displayed by default.
+ displayed by default. -This view supports byte editing.
-Ascii
--+ +The Ascii view shows each byte as its equivalent Ascii character. - For those bytes that do not represent an Ascii character, the format shows +
This view supports byte editing.
+Chars
++- -The Chars view shows each byte (or group of bytes) as its equivalent + character, using a JVM installed charset. Typical examples will be US-ASCII + (1 byte per character), UTF-8 (between 1 and 3 bytes per character), UTF-16 + (typically 2 bytes per character, but can also require 4 bytes for some).
+ +For those bytes that do not encode a valid Unicode character, the view shows it as a tic (".").
-This view supports byte editing.
-Address
--+ +The Address view displays a tic (".") for all bytes whose + +
Some characters, though valid, will not be able to be displayed if the active + font does not support that character. Typically these characters are rendered + as a blank square or some other distinctive shape to indicate the issue.
+ +This view supports byte editing.
+Address
++- -The Address view displays a tic (".") for all bytes whose formed address does not fall within the range of memory for the program. For those addresses that can be formed and are in memory, the view shows the symbol,
- -- So if you go to that address in the Code Browser, and + So if you go to that address in the Code Browser, and make a Pointer data type, the address pointed to is in memory. Conversely, if you go to a "tic" address in the Code Browser and make a pointer, the - address pointed to is not in memory (the operand is - rendered in red).
This view does not support editing.
-Disassembled
--+ +The Disassemble view shows a "box" (
+ +) symbol for each address that has - undefined bytes. For those addresses that are instructions - or defined data, the - view - shows a tic ("."). With this view, you can easily see what areas of the + address pointed to is not in memory (the operand is rendered in red).
This view does not support editing.
+Disassembled
++- -The Disassemble view shows a "box" + (
-) symbol + for each address that has undefined bytes. For those addresses that are + instructions or + defined data, the + view shows a tic ("."). With this view, you can easily see what areas of the program have been disassembled.
This view does not support editing.
-Hex Short
--+ +This format shows two-byte numbers represented as an four-digit hex number.
-This view supports editing. When a byte - is changed, both bytes associated with this address are rendered in - red to denote the change.
-This view does not support editing.
+Hex Integer
---This format shows four-byte numbers represented as an eight-digit hex number.
-This view supports editing. When a byte +
Hex Short
+++ +This format shows two-byte numbers represented as an four-digit hex number.
+ +This view supports editing. When a byte is changed, + both bytes associated with this address are rendered in + red to denote the change.
+Hex Integer
+++ red to denote the change. +This format shows four-byte numbers represented as an eight-digit hex number.
+ +This view supports editing. When a byte is changed, all four bytes associated with this address are rendered in - red to denote the change.
-Hex Long
---This format shows eight-byte numbers represented as an 16-digit hex number.
-This view supports editing. When a byte +
Hex Long
+++ red to denote the change. +This format shows eight-byte numbers represented as an 16-digit hex number.
+This view supports editing. When a byte is changed, all eight bytes associated with this address are rendered in - red to denote the change.
-Hex Long Long
---This format shows 16-byte numbers represented as an 32-digit hex number.
-This view supports editing. When a byte +
Hex Long Long
+++ red to denote the change. +This format shows 16-byte numbers represented as an 32-digit hex number.
+ +This view supports editing. When a byte is changed, all 16 bytes associated with this address are rendered in - red to denote the change.
-Integer
--- -This view shows four-byte numbers represented in decimal format.
-This view does not support editing.
-Octal
--- -The octal view shows each byte as a three character octal value.
-This view supports editing.
-Binary
--+The binary view shows each byte as an eight character binary value.
-This view supports editing.
-Integer
+++ +This view shows four-byte numbers represented in decimal format.
+ +This view does not support editing.
+Octal
+++ +The octal view shows each byte as a three character octal value.
+ +This view supports editing.
+Binary
++The binary view shows each byte as an eight character binary value.
+ +This view supports editing.
+Status Fields
--The labels below the scroll pane that contains the views shows the following information:
--
- -- -
+- -Start -The minimum address of Memory -- -End -The maximum address of Memory -- +Offset -Displayed in decimal, the + +The labels below the scroll pane that contains the views shows the following + information:
+++ This number is calculated when you set the alignment + offset or the number of bytes per line.+ +
-+ +Start +The minimum address of Memory ++ +End +The maximum address of Memory ++ -Offset +Displayed in decimal, the number of bytes added to each block of memory that is being displayed. - This number is calculated when you set the alignment - address or the number of bytes per line. -- - -Insertion -The address of your current cursor location -+ + +Insertion +The address of your current cursor location +- -
Editing Memory
+Editing Memory
-To enable byte editing,
--
-- Toggle the Enable/Disable Edit toolbar button
-- so that it appears pushed-in.
- Click in a view that supports editing, e.g., Hex or Ascii
-- The cursor changes to red to indicate that this view can be edited.
-Changing bytes is allowed only if your cursor is at an address - that does not contain an instruction. If you attempt to change a byte - of an instruction, an "editing not allowed" message is displayed in the - status area of the - tool.
+To enable byte editing,
++
+- Toggle the Enable/Disable Edit toolbar button +
+so that it appears pushed-in.
- Click in a view that supports editing, e.g., Hex or Chars
+- The cursor changes to red to indicate that this view can be edited.
+Changing bytes is allowed only if your cursor is at an address + that does not contain an instruction. If you attempt to change a byte + of an instruction, an "editing not allowed" message is displayed in the + status area of the tool.
-Changed bytes are rendered in red. - This color can be changed via the Byte Viewer Edit Options by double-clicking on - the Edit Color - field.
+Changed bytes are rendered in red. + This color can be changed via the + Byte Viewer Edit Options + by double-clicking on the Edit Color + field.
-Undo the edit by hitting the Undo button (
+ hitting the Redo button () on +
Undo the edit by hitting the Undo button (
) on the tool. The byte reverts to its original value. Redo your edit by - hitting the Redo button (
).
).
-To turn off byte editing, click the - Enable/Disable - Edit toolbar button
-- so that it no longer appears pushed-in.
If - you have two Byte Viewers running, you can connect +
To turn off byte editing, click the Enable/Disable Edit toolbar button +
+ +so that it no longer appears pushed-in.
+ changes in one Byte Viewer, the other will reflect those changes in red.
If you have two Byte Viewers running, you can + connect the two tools for the "Byte Block Edit" event so that when you make - changes - in one Byte Viewer, the other will reflect those changes in red.
Cursor Colors
@@ -222,124 +223,172 @@ color="#000000">black. (Cursor colors can be changed via the Options dialog) If the byte editing is enabled and the view that is in focus supports - editing, - then the cursor is red. + editing, then the cursor is red. -
-Byte Viewer Options:
-The Byte - Viewer Options dialog can be used to add and remove views, set the - Alignment Address, set the - number of bytes per line, - and set the group size to be - used by the hex view. - To launch the Byte Viewer Options dialog, - press the
+icon on the Byte Viewer toolbar.
Byte Viewer Options
++-The Byte Viewer Options dialog can be + used to add and remove views, set the alignment offset, set the number + of bytes per line, and set the group size to be used by the hex + view.
+To launch the Byte Viewer Options dialog, + press the
+icon on the Byte Viewer toolbar.
+
-Alignment Address
--- -The alignment address specifies what address should appear in - column 0. Any address can be specified, but the address will be - normalized to be near the program's minimum address. This enables you to - view bytes in an offcut manner and to identify patterns in the bytes. - Changing the alignment address affects the offset, - which is the column that would display the bytes for address 0 if it - existed. - The offset is affected by both the alignment address and the bytes per - line. The offset is displayed as a label below the scroll - pane containing the views.
--
Sometimes - you might see a byte pattern such that you want all the bytes - to line up in the first column of the display. Consider the cursor - position in the image below. If you want to see the fourth column of - bytes (values of 00) to appear in the first column, you would enter an - alignment address of 0040b003, as indicated by your cursor position.
-
The result of setting the alignment address to 0040b003 - is shown below. The calculated offset is 13, the number of bytes added - to each memory block to create a new alignment. The first line of the - display shows the "remainder" bytes of 16 (bytes per line) divided by - 13, the offset. If you were to put your cursor on the starting byte of - the first line, you would see that your insertion point is 0040b000, in - this example.
--
Set Bytes Per Line
--The bytes per line indicates how many bytes are displayed in one - line in a view. The default value is 16.
-
- All formats shown must be able to support the new value. - For example, since the HexInteger and Integer formats show +
Bytes Per Line
++- -The bytes per line value indicates how many bytes are displayed in one + line in a view. The default value is 16.
+ +-
+ All formats shown must be able to support the new value. + For example, since the HexInteger and Integer formats show bytes in groups of four, the bytes per line must be a multiple of four. If a selected format cannot support a value for the bytes per line, an - error message will appear and the OK - button will be disabled.
Set Group Size
--+ +The group size is the number of bytes that the Hex view shows as + error message will appear and the OK button will be disabled.
++ Alignment Offset
+++ +The alignment offset controls which bytes should appear in column 0. This enables + you to view bytes in an offcut manner and to identify patterns in the bytes. + The offset is displayed as a label below the scroll pane containing the views.
+ ++ +
Sometimes + you might see a byte pattern such that you want all the bytes + to line up in the first column of the display. Consider the cursor position in the + image below:
+ +
If you want the fourth column of bytes (values of 00) to appear in + the first column, you need to decrease the alignment offset by 3, either by using + the Shift Bytes Left popup action (or hitting Ctrl+Comma) a few times, + or by changing the alignment offset value in the configuration dialog.
+ +The result of setting the alignment offset is shown below.
+ ++
Hex Group Size
++- -The group size is the number of bytes that the Hex view shows as a "unit." For example, a group size of two means to show two bytes - grouped together with no spaces.
-View Selection
--+ +Each potential view is listed as a checkbox. Select the - checkboxes corresponding to the views to be shown. Red text + grouped together with no spaces.
+Charset
+++ +This specifies how bytes are converted into characters. Typically + US-ASCII or UTF-8 are good choices, but other charsets + that are installed in your Java JVM will be available also.
+ +Character encoding schemes that use more than 1 byte to encode a + character are supported, but can produce incorrect values interlaced + with the actual text characters that should be there, as each byte is treated + as the starting position of a character, even if it was already 'used' by + a previous multi-byte character.
+ +For instance, if a sequence of UTF-8 characters are being displayed, and + some characters take 1 byte while some take 2 or 3, the positions in the + Chars display grid that are not valid starting locations may display + garbage, or just a "." dot indicating the byte could not be decoded.
+ +Character encoding schemes that rely on escape sequences to shift between + code pages will not produce useful values in the Chars display grid as + each byte (and some number of following bytes) are treated as a unique string + to decode and will not include any previous escape sequences that would be needed + to correctly decode the bytes in question.
+ +The available charsets are listed in a dialog that is displayed + when clicking the "..." browse button. You can filter by name, sizes, + supported scripts, etc by using the standard table filter mechanisms.
+Compact Char Width
+++ +This option toggles between wide and narrow display of characters. If + displaying ASCII or other Latin alphabet based text, the narrow option should + be sufficient to be able to see each character.
+ +If displaying data that has non-Latin alphabets (scripts) the wide option + will prevent characters from overwriting their neighbors.
+Use Char Alignment
+++ +This option will only display characters for fixed-width multi-byte character + sets (such as UTF-16, UTF-32) starting at aligned offsets, avoiding converting + bytes sequences that are not aligned with the charset alignment size.
+ +Use the alignment offset option to control + where the starting offset for aligned characters is located.
+ +NOTE: UTF-16 is not technically a fixed-width character encoding, as a + single character may need 2 or 4 bytes to be encoded. However, it is + self-synchronizing and the 2nd half of a 4 byte sequence should be ignored and + rendered as a "." dot.
+ +NOTE: An align-able charset needs to be marked with its alignment size in + the charset_info.json configuration file, and by default only UTF-16 and + UTF-32 have these values.
+View Selection
+++Each potential view is listed as a checkbox. Select the + checkboxes corresponding to the views to be shown. Red text indicates a view cannot be displayed since it doesn't support the specified number of bytes per line.
-Reorder / Resize Views
The various views in the ByteViewer can be reordered by dragging the view header to the left or right of its current position. The view - positions are swapped.
+ positions are swapped. +The width of each view can also be changed by dragging the separator bars in the view header to the left or right. This will resize the view that is to - the left of the separator bar.
+ the left of the separator bar.
Writing Your Own Format Plugin
--To supply your own format to be added to the list of views +
To supply your own format to be added to the list of views displayed in the Byte Viewer,
-
+ to add your class files to your classpath in your development environment. +- Write an implementation of the ghidra.app.plugin.core.format.DataFormatModel interface, - which determines the format of how the bytes should be - represented.
-- Edit your Plugin +
- Write an implementation of the ghidra.app.plugin.core.format.DataFormatModel interface, + which determines the format of how the bytes should be represented.
+- Edit your Plugin path to include your class files if you are running Ghidra in production mode versus development mode; in development mode, you will have - to add your class files to your classpath in your development - environment.
-- Restart Ghidra.
-Restart Ghidra. +-
Provided by: Byte Viewer Plugin
- +Provided by: Byte Viewer Plugin
+ + +