Closed nomadbyte closed 1 year ago
For ZD2 effects the Icon is contained within the effect file, and can be extracted with the decode_effect.py
script.
https://github.com/mungewell/zoom-zt2/blob/master/decode_effect.py#L30
Are you saying that the unit display a different icon, maybe the one in the ICON
section is just got GuitarLab. You can also extract the CODE
section which is an ELF binary.... you could check that for the "narrow" bitmap.
Also double check that you are looking at the right file, seems that there are several with similar naming...
zoom_fx_AllZDL7$ find . -name 'MACH301U.ZD2' -exec md5sum {} \;
1557365fe45e0153cdf59e4144474bdc ./G1X FOUR/unzipped/MACH301U.ZD2
1557365fe45e0153cdf59e4144474bdc ./G1 FOUR/unzipped/MACH301U.ZD2
1557365fe45e0153cdf59e4144474bdc ./R20/unzipped/MACH301U.ZD2
1557365fe45e0153cdf59e4144474bdc ./H8/unzipped/MACH301U.ZD2
zoom_fx_AllZDL7$ grep "MATCH" master.txt
0x040000a0 : MATCH30 (v1.10, 34.86%), 0x4e53b71acf5e94bfa85c648dd285e5a5
0x040000a1 : MATCH30 (v1.00, 34.86%), 0x1557365fe45e0153cdf59e4144474bdc
0x040000a2 : MATCH30 (v1.00, 31.92%), 0xd01bd654453b2648839acac29255f104
Well, if you look in my message above, the icon in the ZD2 header (BM-section) is different from the one on the device (the blue-shaded pic above). The actual ZD2 file on the device is:
1557365fe45e0153cdf59e4144474bdc ./G1 FOUR/unzipped/MACH301U.ZD2
It must have the other icon encoded somewhere other than the BM-section in the header, hard to say in which format though (?? ICO, the size is about 23x30 dots or more if there's any padding around). The ToneLib editor appears to show the BM-icon not the icon on the device.
Yes, I did read you post, and suggest a place to look (in the CODE
section of the ZD2). Anyhow, now I am home I was able to confirm your observation. The 'on device' icon does NOT match the ICON
for MATCH30 on my G1X Four.
The CODE
section can be extracted with, and the processed with arm-linux-gnueabi-objdump
.
zoom_fx_AllZDL7$ python3 ../decode_effect.py -c test.code ./G1\ FOUR/unzipped/MACH301U.ZD2
It may be that the bitmap is just a 'raw' section of data. That was the case for the pedal name/icon/boot screen. See: https://github.com/mungewell/zoom-zt2/issues/18#issuecomment-748546089
For reference the other ZD2's I mentioned are used on the following devices.
zoom_fx_AllZDL7$ find . -name 'list_sorted.txt' -exec grep 0x4e53b71acf5e94bfa85c648dd285e5a5 {} \;
0x040000a0 : MATCH30 (v1.10, 34.86%), 0x4e53b71acf5e94bfa85c648dd285e5a5, ./G3n/unzipped/MATCH_30.ZD2
0x040000a0 : MATCH30 (v1.10, 34.86%), 0x4e53b71acf5e94bfa85c648dd285e5a5, ./G5n/unzipped/MATCH_30.ZD2
0x040000a0 : MATCH30 (v1.10, 34.86%), 0x4e53b71acf5e94bfa85c648dd285e5a5, ./G3Xn/unzipped/MATCH_30.ZD2
zoom_fx_AllZDL7$ find . -name 'list_sorted.txt' -exec grep 0x1557365fe45e0153cdf59e4144474bdc {} \;
0x040000a1 : MATCH30 (v1.00, 34.86%), 0x1557365fe45e0153cdf59e4144474bdc, ./G1X FOUR/unzipped/MACH301U.ZD2
0x040000a1 : MATCH30 (v1.00, 34.86%), 0x1557365fe45e0153cdf59e4144474bdc, ./G1 FOUR/unzipped/MACH301U.ZD2
0x040000a1 : MATCH30 (v1.00, 34.86%), 0x1557365fe45e0153cdf59e4144474bdc, ./R20/unzipped/MACH301U.ZD2
0x040000a1 : MATCH30 (v1.00, 34.86%), 0x1557365fe45e0153cdf59e4144474bdc, ./H8/unzipped/MACH301U.ZD2
zoom_fx_AllZDL7$ find . -name 'list_sorted.txt' -exec grep 0xd01bd654453b2648839acac29255f104 {} \;
0x040000a2 : MATCH30 (v1.00, 31.92%), 0xd01bd654453b2648839acac29255f104, ./G11/unzipped/MACH30AU.ZD2
0x040000a2 : MATCH30 (v1.00, 31.92%), 0xd01bd654453b2648839acac29255f104, ./G6/unzipped/MACH30AU.ZD2
There are a few ZD2 effects which have updated revisions, perhaps these would be a good way to compare the CODE
sections... assuming that the bitmap is unchanged. You might be able to hunt down a section which is not changed between the two files.
I have previously found this util to be quiet helpful... https://github.com/Sepero/SearchBin
The icon is in the CODE
section, although I have not tracked down exactly where... using objcopy
to extract the .const
section and then converting that as a 8x3200 monochrome image.
$ arm-linux-gnueabi-objcopy -j .const -S -w -K effectTypeImageInfo* -I elf32-little test.code -O elf32-little test_mod.code
$ arm-linux-gnueabi-objcopy -I elf32-little test_mod.code -O binary test.bin
$ convert -monochrome -size 8x3200 -depth 1 MONO:test.bin test.png
and you get:
Couldn't figure out how to extract just one symbol, so have to do some tricks with dd
.
$ arm-linux-gnueabi-objcopy -j .const -S -w -K picTotalDisplay_* -I elf32-little test.code -O elf32-little test_mod.code
$ arm-linux-gnueabi-objcopy -I elf32-little test_mod.code -O binary test.bin
$ arm-linux-gnueabi-objdump -t test_mod.code
test_mod.code: file format elf32-little
SYMBOL TABLE:
80000000 l d .const 00000000 .const
800006b0 g O .const 0000005c .hidden picTotalDisplay_MATCH_30
$ dd if=test.bin of=test2.bin bs=1 skip=1712 count=108
$ convert -monochrome -size 8x108 -depth 1 MONO:test2.bin -transpose -crop 23x8+0+0 stripe1.png
$ convert -monochrome -size 8x108 -depth 1 MONO:test2.bin -transpose -crop 23x8+23+0 stripe2.png
$ convert -monochrome -size 8x108 -depth 1 MONO:test2.bin -transpose -crop 23x8+46+0 stripe3.png
$ convert -monochrome -size 8x108 -depth 1 MONO:test2.bin -transpose -crop 23x8+69+0 stripe4.png
$ convert stripe1.png stripe2.png stripe3.png stripe4.png -append icon.png
$ display icon.png
What a find!!
I too realized that the icon must be encoded in the ELF somewhere but short of knowing the format it's was next to impossible to spot it in hex. I almost suspected that these could be literally drawn on-device using some primitives.... luckily it was not the case (would be quite wasteful too).
I'm impressed!! (and quite a bit relieved, knowing that indeed the icon data is there). Trying to make sense out of your commands.
Just to give you some context of my inquiry. The MACH301U.ZD2
icon that you extracted is a 1U (single-unit width) icon, for a wide (aka two-slot, 8-param) G5n module -- MATCH_30.ZD2
and which, I guess, contains the "wide" 2U version of the icon. The 1U-variants also restructure the parameter sets to accommodate the paging (by inserting Dummy params). As such 1U icons fit properly on G1/G1X type of displays which allow only 5 icons, all 1U sized.
So, I can't prove that the MATCH_30.ZD2
contains a wide on-device icon, just a guess. You may readily extract the icon to see if this is the case indeed.
Meanwhile, another 1U module variant EGFLTR1U.ZD2
(it's targeted for G6, the G5n version is EGFILTER.ZD2
), appears to be a hybrid -- it has 1U set of parameters, yet it displays a wide two-slot on-device icon, which kinda messes up the look on G1/G1X-sized display.
So, just an idea, I was wondering if it would make any sense to try to locate and replace the on-device icon, thus fully adapting the EGFLTR1U.ZD2
for G1/G1X use. Nothing special about it, just a curious module meant to operate in STOMP mode, it selectively engages HFP/LFP filters.
Ok, now this all looks familiar. The bytes corresponding to the on-device icon seem to begin with fe 0...
. I did see them before, but the bit-order did not look anything resembling the expected pixels... No wonder, as the bitmap is sliced by byte-wide fragments and flipped, not sure why. There must be some transformation to apply, so that it could be stitched together.
From your steps I gather that the data is in the .const
section of the .ELF module and is referenced to picTotalDisplay_${module-name}
. So the section could then be extracted, and the icon-bytes will be at their offset in the section. Convert (ImageMagick) then is used to normalize the image.
I kind of rehashed the steps using more familiar to me tools and applied it to the G5n MATCH_30.ZD2
variant (wide). It all comes down to combining the referenced offsets (ZD2[ELF]:0x240
, ELF[.const]:0x49a0
, .const[picTotalDisplay_]:0x6b0
) and the object size:188
)
$ hd MATCH_30.ZD2 | grep "ELF"
00000240 7f 45 4c 46 01 01 01 40 00 00 00 00 00 00 00 00 |.ELF...@........|
$ tail -c +$((16#240+1)) MATCH_30.ZD2 >tmp.elf ; readelf -a tmp.elf | grep -e "\] .const" -e " [_]*picTotalDisplay_" ; rm tmp.elf
[13] .const PROGBITS 80000000 0049a0 000d0d 00 A 0 0 8
275: 800006b0 188 OBJECT GLOBAL HIDDEN 13 picTotalDisplay_MATCH_30
$ xxd -p -s $((16#240+16#049a0+16#06b0)) -l 188 MATCH_30.ZD2 | xxd -r -ps > icon.bin
$ convert -monochrome -size 8x188 -depth 1 MONO:icon.bin icon.png
The G5n icon is wider (188 bytes vs 92 for 1U-size), it's split into 4 vertical slices, so the stitched width is 188/4=47px, and height 8*4=32px, thus 47x32px. The 1U icon is 23x32px.
Below are the extracted slices (convert
can normalize it too, perhaps even in one shot, but it's too much to RTFM for now). As could be noticed, this icon looks just like the one from the ZD2 BM-section, except for the 2px whitespace crop at the bottom to make it 47x30px.
Applying the same steps to the mentioned EGFLTR1U.ZD2
(G6 targeted), yields an even wider icon 50x32px, it's more than twice wider than 1U. By the way, in this case the .const object name is with a leading underscore _picTotalDisplay
.
.
Not sure if it's possible at all to stuff a smaller 1U icon into the ELF and then recraft the ZD2 module, so that it would be fully compatible with G1/B1/A1 FOUR models?
picTotalDisplay
is a 'private' reference to the location of a blob of raw pixel data. There is likely a 'public' function that makes this available to host OS, or performs the writing to the LCD.
I don't think that the effort is worth it, but you could look further into the ELF to try to find it.
On the arrangement (slicing) of the pixels, this is probably just how the LCD screen is memory mapped. So code just has to copy chunks of memory around.
cleaned up the process with a little automation.... icon_it.sh.txt
$ bash icon_it.sh ./G1\ FOUR/unzipped/BLACKOPT.ZD2
Another interesting observation. The first byte of the effectTypeImageInfo
block appears to be the width of the 'on-device' icon.
./G11/unzipped/MACH30AU.ZD2
800005b0 l O .const 00000130 .hidden effectTypeImageInfo
000005b0 2f 00 00 00 1e 00 00 00 e0 06 00 80 00 00 00 00 |/...............|
./G5n/unzipped/MATCH_30.ZD2
80000580 l O .const 00000130 .hidden effectTypeImageInfo
00000580 2f 00 00 00 1e 00 00 00 b0 06 00 80 00 00 00 00 |/...............|
./G1\ FOUR/unzipped/MACH301U.ZD2
80000580 l O .const 00000130 .hidden effectTypeImageInfo
00000580 17 00 00 00 1e 00 00 00 b0 06 00 80 00 00 00 00 |................|
Probably need to validate this across more ZD2s.
./G6/unzipped/EGFLTR1U.ZD2
80000268 l O .const 00000130 .hidden effectTypeImageInfo
00000260 00 00 00 00 00 00 00 00 32 00 00 00 1e 00 00 00 |........2.......|
0x32 = 50
Icon is 50 pixels wide.
I really appreciate your effort, it's fun working with you on this!
... looks like, it's the first 4-byte int for width ([17]=23, [2f]=47, [32]=50
), the second 4-byte int for height: [1e]=30
, even though the encoded image (in 4 slices) adds up to height 32pixels, apparently it's sized to 30, just as the icon in the BM-section of the ZD2. If you look next, what follows, the bytes 1U:[b0 06]=0x06b0, AU:[e0 06]=0x06e0, EGFLTR1U:[98 03]=0x0398
, appear to refer to the offset of the picture data from the start of the .const
section.
NOTE: both MATCH 30 versions (2U and 1U) have the same offset for the picture data 0x06b0
(picTotalDisplay_MATCH_30
).
There are more details follow that part, just a guess, those bytes describe the picture slices. I'll try to compare those between 2U/1U, if there is any pattern.
Also, if you examine the actual bytes following the on-device icon, it looks like there's another chunk(s) of graphical data encoded there, my guess it may be the "selected effect" icon, that one which is shaded when pressing the left-pedal foot-switch, just a guess, but it does not anything recognizable.
I wonder, if indeed, the picTotalDisplay/effectTypeImageInfo
logic does rely on the stated ints for sizing the icon data, then supplanting it for narrower icon may be rather simple by fixing the width value and overwriting the icon's bytes, padding the rest with [FF] for white color. I guess a test may be just to fix the width and see if the wide icon shows up only to the set size.
Here it is. Patched the 1U icon from RNDMFLTR.ZD2
(22x32px, 88bytes) over the existing icon bytes in EGFLTR1U.ZD2
, the remaining bytes of the original 2U+ icon just as they were, and also patched the sizing info bytes (width:[16], height:[20]).
Next, will try my hand at pixel-art to etch EG legibly instead of RNDM :) Not sure if there's any image format that's easy to transpose into the raw bytes.
Cool that it worked OK. I assume you hand patch the bytes, rather than using a tool to re-write the ELF file?
It should be fairly easy to use convert
to go from PNG to RAW bits (with the striping/rotation).
Realized that the 'extract' script didn't need a special ARM objdump/objcopy, so I checked it in here: https://github.com/mungewell/zoom-zt2/blob/master/extract_device_icon.sh
... I assume you hand patch the bytes, rather than using a tool to re-write the ELF file?
How would you go about rewriting the ELF to pack in only the part we figured out, objcopy
? Or you meant repacking the patched ELF part into ZD2? I just patched the ZD2 directly skipping the intermediary step, loosely scripted.
I guess, this could be fully scripted to copy an icon from a given input ZD2 into a compatible output ZD2, well, maybe it would be the next step, though not sure how many 1U ZD2 need such adaptation. My next step will be etching that "EG" nicely... by hand, though I'm looking if the byte-sequence for letters E and G could be lifted from another icon.
Yes, it has been fun. Although this specific application is perhaps not so useful to me, I think that the effectTypeImageInfo
block may contain whether the effect is mono or stereo input/output... something that I have been looking for .
Just for more 'fun' I checked in a little tool to convert PNG to raw striped bytes,
$ bash extract_device_icon.sh zoom_fx_AllZDL7/R20/unzipped/EQ_AVN.ZD2
objcopy: test.code: warning: Empty loadable segment detected at vaddr=0, is this intentional?
test_mod.code: file format elf32-little
SYMBOL TABLE:
80000398 l O .const 0000005c .hidden picTotalDisplay_GraphicEQ
80000398 l d .const 00000000 .hidden .const
80000000 l d .const 00000000 .const
92+0 records in
92+0 records out
92 bytes copied, 0.000277523 s, 332 kB/s
icon width is 23
$ bash restripe_device_icon.sh icon.png
Files icon.raw and icon2.raw are identical
Do we think that this bug is now closed?
Great work! Thanks.
Added the -E
flag to help pushing modified ELF code directly into the ZD2 file....
$ python3 decode_effect.py --help
usage: decode_effect [-h] [-d] [-s] [-m] [-b BITMAP] [-i INFO] [-c CODE] [-j]
[-x XML] [-t TEXT] [-D DONOR] [-B] [-T] [-I] [-C] [-X]
[-F] [-E] [-V] [-o OUTPUT]
FILE
positional arguments:
FILE File to process
optional arguments:
-h, --help show this help message and exit
-d, --dump dump configuration to text
-s, --summary summarized configuration in human readable form
-m, --md5sum include md5sum of file in summary report
-b BITMAP, --bitmap BITMAP
extract Icon/Bitmap to FILE
-i INFO, --info INFO extract Info to FILE
-c CODE, --code CODE extract Code to FILE
Language:
Extract either English or Japanese segments
-j, --japan select Japanese version for export
-x XML, --xml XML extract XML to FILE
-t TEXT, --text TEXT extract Text to FILE
Donor:
Take replacement sections from a donor ZD2
-D DONOR, --donor DONOR
specify donor ZD2 FILE
-B, --donor-bitmap extract Icon/Bitmap from donor
-T, --donor-text extract Text from donor
-I, --donor-info extract Info from donor
-C, --donor-code extract Code from donor
-X, --donor-xml extract XML from donor
-F, --donor-final extract FinalBytes from donor
-E, --donor-elf replace Code with ELF file (ie whole file)
-V, --crc validate CRC32 checksum
-o OUTPUT, --output OUTPUT
output combined result to FILE
$ python3 decode_effect.py -D MODIFED.code -E -o ZNR_MOD.ZD2 ZNR.ZD2
Checksum Recalculated: 0xca8343be
Looks like there a Python project which has tools for directly (binary) editing ELF files. https://github.com/Gallopsled/pwntools http://docs.pwntools.com/en/latest/elf/elf.html
Well that turned out well.....
$ python3 decode_effect.py -c ZNR.ZD2.code ZNR.ZD2
$ python3
Python 3.6.9 (default, Nov 25 2022, 14:10:45)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> from hexdump import *
>>> code = ELF("ZNR.ZD2.code")
[!] Could not populate PLT: 'int' object has no attribute 'lower'
[*] 'ZNR.ZD2.code'
Arch: 140-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
>>> hexdump(code.read(code.symbols["effectTypeImageInfo"], 16))
00000000: 17 00 00 00 1E 00 00 00 80 02 00 80 14 00 00 00 ................
>>> code.write(code.symbols["effectTypeImageInfo"], bytes([0x16, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00]))
>>> code.save()
>>> quit()
$ python3
Python 3.6.9 (default, Nov 25 2022, 14:10:45)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> from hexdump import *
>>> code = ELF("ZNR.ZD2.code")
[!] Could not populate PLT: 'int' object has no attribute 'lower'
[*] '/home/simon/zoom-zt2-sdw-github/ZNR.ZD2.code'
Arch: 140-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
>>> hexdump(code.read(code.symbols["effectTypeImageInfo"], 16))
00000000: 16 00 00 00 1D 00 00 00 80 02 00 80 14 00 00 00 ................
>>> quit()
Seems our Russian friend has a break-down/explanation of the "effect type info" used in ZDL effects. May give us some hints. https://github.com/ELynx/zoom-fx-modding/blob/main/library/CH_1.md#optional-knob-positions
Has anyone figured out the location of the single-width icons (assuming these are encoded in the ZD2 file)? This is the icon that appears on the G1/B1/A1 FOUR screens.
Just to make it clear, the BM-section (
@0x88
) is not the icon that is being shown on-the-device, at least in the case of G1/B1/A1 models.For example, for MATCH30 (
MACH301U.ZD2
) the on-device icon has wording MTCH30 and some accent details, while the BM-section shows an icon (302 bytes) with MATCH30 wording, and it's wider.The on-device icon (about 23x30px):![g1four-mach301u-icon](https://user-images.githubusercontent.com/1738281/210154871-5eb3301d-c6ba-447f-947f-43172b532996.png)
NOT (the BM-section icon: 47x30px):