diff --git a/applications/plugins/wii_ec_anal/LICENSE b/applications/plugins/wii_ec_anal/LICENSE
new file mode 100644
index 000000000..95e544a06
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 BlueChip
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/applications/plugins/wii_ec_anal/README.md b/applications/plugins/wii_ec_anal/README.md
new file mode 100644
index 000000000..8d439c7e0
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/README.md
@@ -0,0 +1,234 @@
+# [FlipperZero] Wii Extension Controller Protocol Analyser
+This Protocol Analyser offers a full Test and Calibrate system for Wii Extension Controllers.
+
+__Disclaimer:__ *Use of this plugin, and notably connecting an Extension Controller to the FlipperZero is performed entirely at your own risk.*
+
+# Notes
+This plugin has (todate) only been tested with official Nintendo Nunchucks and Classic Controllers - namely Nunchucks and Classic Controllers.
+
+# Encryption
+This plugin has SOME code to handle encryption, but it it unused, untested, and some of it is known to un-work.
+
+This plugin (currently) only works with Extension Controllers which implement the encryption-bypass strategy. IE. `i2c_write(0xf0, 0x55) ; i2c_write(0xfb, 0x00)`
+
+If you need this functionality, either raise an Issue or, better still, a Pull Request.
+
+# Screen: SPLASH
+
+The SPLASH Screen is displayed when the Plugin starts. It can be cleared by pressing any key, else it will auto-clear after 3.5 seconds.
+
+# Screen: WAIT
+
+The WAIT screen will display which pins you need to connect between the flipper and the Wii Extension Controller.
+
+__Disclaimer:__ Use of this plugin, and notably connecting the Controller to the FlipperZero is performed entirely at your own risk.
+
+Looking in to the exposed side of the Extension Controller plug, with the notch on the bottom
+
+| EC Pin # | EC Position | EC Pin ID | Pin Function | FZ GPIO Pin Name | FZ GPIO Pin # |
+| :---: | :---: | :---: | :---: | :---: | :---: |
+| 1 | top-left | +3v3 | Power | 3v3 | 9 |
+| 2 | bottom-left | SCL | i2c clock | C0 | 16 |
+| 3 | top-centre | EN | ¿detect? | | |
+| 4 | bottom-centre | -x- | -none- | | |
+| 5 | top-right | SDA | i2c data | C1 | 15 |
+| 6 | bottom-right | Gnd | Power | Gnd | 18 |
+
+Keys:
+* Left - Show splash screen
+* Back - exit plugin
+
+The easiest way to connect a Wii Extension Controller to a FlipperZero is arguably with a ["WiiChuck"](https://www.ebay.co.uk/sch/?_nkw=wiichuck) or a ["Nunchucky"](https://www.solarbotics.com/product/31040)
+
+
+### ** WARNING **
+Neither the WiiChuck, nor the Nunchucky have a pin polarisation mechanism.
+If you plug the adaptor in the wrong way around you WILL apply voltage to the Controller the wrong way round!!
+I have no idea if THIS WILL PERMANENTLY KILL THE CONTROLLER ...Who wants to try it?
+
+On all the WiiChucks I have seen:
+* The WiiChuck has THREE connectors on one side, and TWO connectors on the other.
+* The side with TWO connectors should go against the side of the Controller plug with the big indent.
+```
++-------------+
+| _________ |
+| | = = = | |
+| |_=_____=_| | <-- notice missing pin
+| ___ |
+| | | | <-- notice indent
++----+ +----+
+```
+
+
+...BUT I *highly* recommend you check the pins on your adaptor to make sure everything goes well.
+
+I believe the unconnected pin on the top is a "presence detect" function, but I have not (yet) verified this.
+This feature is NOT required by this plugin, as the detection is performed by means of an i2c handshake.
+
+When a device is connected it will be immediately recognised. If it is not, either:
+* The Controller is not correctly connected
+...This may be as simple as a broken wire.
+* The controller board in the Controller is faulty.
+...Repair of which is beyond the scope of this document.
+
+To get the list of "known" Controllers, run `./info.sh`
+As of writing this, that returns:
+```c
+[PID_UNKNOWN ] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Unknown Perhipheral", SCENE_DUMP,
+[PID_NUNCHUCK ] = { {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, "Nunchuck", SCENE_NUNCHUCK,
+[PID_CLASSIC ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x01}, "Classic Controller", SCENE_CLASSIC,
+[PID_BALANCE ] = { {0x00, 0x00, 0xA4, 0x20, 0x04, 0x02}, "Balance Board", SCENE_DUMP,
+[PID_GH_GUITAR ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero Guitar", SCENE_DUMP,
+[PID_GH_DRUMS ] = { {0x01, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero World Tour Drums", SCENE_DUMP,
+[PID_TURNTABLE ] = { {0x03, 0x00, 0xA4, 0x20, 0x01, 0x03}, "DJ Hero Turntable", SCENE_DUMP,
+[PID_TAIKO_DRUMS] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x11}, "Taiko Drum Controller)", SCENE_DUMP,
+
+```
+
+You can see that there are EIGHT known devices. One is the default for an unknown controller; SEVEN devices are known by name; and TWO (of those seven) have bespoke "scenes" (ie. SCENE_NUNCHUCK & SCENE_CLASSIC).
+
+# Screen: NUNCHUCK - MAIN
+
+When you connect a Nunchuck, you will see a screen displaying:
+* Accelerometer{X,Y,Z} values
+* Joystick{X,Y} values
+* Joystick graphic
+* Button{C,Z}
+
+Keys:
+* Left - Go to the DUMP screen
+* Right - Go to the NUNCHUCK_ACC accelerometers screen
+* Up/Down/OK - [qv. Peak Meters]
+* Short-Back - Reset controller
+* Long-Back - Exit plugin
+
+# Screen: NUNCHUCK - ACCELEROMETERS
+
+
+
+| Axis | Movement | Lower | Higher |
+| :---: | :---: | :---: | :---: |
+| X | Left / Right | Left | Right |
+| Y | Fwd / Bkwd | Fwd | Bkwd |
+| Z | Down / Up | Down | Up |
+
+* Movement in the direction of an axis changes that axis reading
+* Twisting/tilting around an axis changes the other two readings
+* EG.
+ * Move left (along the X axis) will effect X
+ * Turn left (a rotation around the Y axis) will effect X and Z
+
+Keys:
+* Left - go to the main NUNCHUCK screen
+* Up
+ * Auto-Pause Disabled --> Enable Auto-Pause
+ * Paused at the end of a page --> Restart scanner
+ * Running with Auto-Pause Enabled --> Disable Auto-Pause
+* Nunchuck-Z - Toggle pause
+* Nunchuck-C - Toggle auto-pause
+* Long-OK - Enter Software Calibration mode [qv. Calibration]
+ * Calibration mode on the Accelerometer screen will ONLY calibrate the accelerometer
+* Short-OK - Leave Software Calibration mode *and* Calibrate CENTRE position(s)
+* Short-Back - Reset controller
+* Long-Back - Exit plugin
+
+NB. Code DOES exist to scroll the display, but the LCD refresh rate is too low, and it looks awful
+
+# Screen: CLASSIC
+
+When you connect a Classic Controller [Pro], you will see a screen displaying a Classic Controller
+* The Classic Controller will animate in line with controller events
+* The scan rate is set to 30fps, but in reality there is a bit of lag with the LCD screen, so YMMV.
+
+Keys:
+* Left - go to the DUMP screen
+* Right - show analogue readings (Left to hide them again)
+* Up/Down/OK - [qv. Peak Meters]
+* Short-Back - Reset controller
+* Long-Back - Exit plugin
+
+# Screen: DUMP
+
+The Dump screen will show you the raw readings from the device.
+If you connect a device which does not have a bespoke `_decode()` function (etc.), you will see (only) this screen.
+* SID - String ID - human-readable name (from the `info` table)
+* PID - Peripheral ID - The 6 bytes which identify the device.
+* Cal - Calibration data - 16 bytes
+* The bottom row of hex shows the SIX bytes of Controller data
+ * Below each hex digit is the binary representation of that digit
+ * By example. With a Nunchuck connected, click the Z button, and watch the bit on the far right
+
+Keys:
+* Right - return to controller-specific screen (if there is one)
+* Short-Back - Reset controller
+* Long-Back - Exit plugin
+
+# Peak Meters (Calibration values)
+
+On any Controller-specific screen with a Peak/Trough menu displayed:
+* Up - [toggle] only show peak values
+* Down - [toggle] only show trough values
+* Long-OK - Enter Software Calibration mode [qv. Calibration]
+* Short-OK - Leave Software Calibration mode / Calibrate CENTRE position(s)
+
+# Calibration
+
+
+* __This project handles Calibration of Analogue Controls, but has NO understanding of Accelerometer values (yet).__
+
+Digital buttons do NOT require Calibration.
+
+Some Calibration data is calculated at the factory, and stored in memory (¿OTP?) on the Controller.
+
+Each device has a different way to interpret the Calibration Data.
+EG. A Nunchuck has one joystick, and an accelerometer ...whereas a Classic Controller has 2 joysticks and 2 analogue buttons.
+
+I have personally found the calibration data to be inaccurate (when compared to actual readings), I guess Controllers drift over the years‽
+If the factory-values LIMIT movement, this is easily resolved - by expanding them on-the-fly.
+BUT, I have seen Controllers with factory calibration data that suggests the limits are FURTHER than the joystick can reach ...and this requires a full re-calibration of the Controller!
+
+Probably the best way to calibrate is to:
+* Take a/some reading(s) while the Controller is 'at rest', IE. perfectly still and level.
+* Move the Controller to all extremes and store the extreme {peak/trough} values.
+
+Nintendo (allegedly) take the 'at rest' reading immediately after the Controller is connected, and a 're-calibration' can be performed at any time by pressing {`A`, `B`, `+`, `-`} at the same time, for at least 3 seconds. Although I have no details on what this actually does.
+
+### This tool calibrates as such:
+* When the Controller is first recognised
+ * The factory Calibration data is used to decide the Centre/Middle position and extreme values (eg. far-left & far-right) for each analogue Control
+* Long-OK button press (on the FlipperZero) ...Do NOT touch ANY of the analogue controllers while you are pressing Long-OK
+ * Start the calibrate button flashing
+ * Take the current reading as the Centre position
+ * Set the range limits to "no range"
+ * You must now move the Control between its extremes, so the code can work out the new Calibration/range/peak+trough values
+ * When done, press Short-OK to end Software Calibration mode
+* Short-OK button press (on the FlipperZero) ...Do NOT touch ANY of the analogue controllers while you are pressing Short-OK
+ * Stop the calibrate button flashing
+ * Calibrate the centre position of all analogue controls (accelerometers not supported (yet))
+
+# Screen: DEBUG
+
+On any screen (except SPLASH) you may press Long-Down to enter Debug mode.
+
+You can (at any time) attach to the FlipperZero (via USB) with a serial console {`minicom`, `putty`, whatever} and start the `log` function to see the debug messages.
+
+When you enter the DEBUG screen, the real-time scanner will be stopped. And the following keys made available:
+* Up - Attempt to initialise the attached Controller
+* OK - Take a reading from the attached Controller
+* Long-Down - Restart the real-time scanner and return to the WAIT screen
+
+You can limit the messages at compile-time [see `./info.sh`], or at runtime [FZ->Settings->System->LogLevel]
+
+[This is probably irrelevant since the introduction of FAP support]
+If you have memory issues, limiting the messages at compile-time will make the plugin smaller.
+But (¿obviously?) the more you limit the messsages, the less debug information will be sent to the logger.
+
+# TODO
+
+* FZ Bug: At the time of writing this, there are problems with the i2c FZ functions [qv `i2c_workaround.c`]
+
diff --git a/applications/plugins/wii_ec_anal/README.txt b/applications/plugins/wii_ec_anal/README.txt
new file mode 100644
index 000000000..e7ebe7a4c
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/README.txt
@@ -0,0 +1,67 @@
+ ,-------.
+---( Files )---
+ `-------'
+
+ README.md - User Manual : Body [github markdown]
+ _images/ - User Manual : Images
+ _images/GIMP/ - User Manual : GIMP image masters
+
+ LICENSE - Tech Docs : MIT Licence file
+ README.txt - Tech Docs : Dev notes
+ notes.txt - Tech Docs : Random dev notes
+ info.sh - Tech Docs : Retrieve info from source code
+
+ application.fam - FAP : Header file
+ WiiEC.png - FAP : Icon {10x10}
+
+ gfx/ - Analyser : Images [generated by bc_image_tool]
+ wii_anal.c|h - Analyser : Main application
+ wii_anal_ec.c|h - Analyser : Extension controller actions
+ wii_anal_keys.c|h - Analyser : Keyboard handling
+ wii_anal_lcd.c|h - Analyser : LCD handling
+
+ i2c_workaround.h - Temporary workaround for i2c bug in FZ code
+ err.h - Errors
+ bc_logging.h - Logging macros - especially LOG_LEVEL
+
+ wii_i2c.c|h - i2c functionality
+
+ wii_ec.c|h - Extension Controller basic functions
+ wii_ec_macros.h - Bespoke Extension Controller handy-dandy MACROs
+ wii_ec_classic.c|h - EC: Classic Controller Pro scene
+ wii_ec_nunchuck.c|h - EC: Nunchuck scene
+ wii_ec_udraw.c|h - EC: UDraw scene - not written
+
+ ,----------------------------------.
+---( Adding a new Extension Controller )---
+ `----------------------------------'
+
+//! I'll finish this when I write the UDraw code
+
+Create a new Extension Controller called "mydev"
+
+Create wii_ec_mydev.c and wii_ec_mydev.h
+
+In wii_ec_mydev.c|h
+ Create the functions [& prototypes]
+ bool mydev_init (wiiEC_t* const) ; // Additional initialisation code
+ void mydev_decode (wiiEC_t* const) ; // Decode controller input data
+ void mydev_msg (wiiEC_t* const, FuriMessageQueue* const) ; // Put event messages in the event queue
+ void mydev_calib (wiiEC_t* const, ecCalib_t) ; // Controller calibration function
+ void mydev_show (Canvas* const, state_t* const) ; // Scene LCD display
+ bool mydev_key (const eventMsg_t* const, state_t* const) ; // Scene key controls
+
+In wii_ec.h
+ Include the new header
+ #include "wii_ec_mydev.h"
+ Add a perhipheral id to enum ecPid
+ PID_MYDEV
+
+In wii_anal.h
+ As a scene name to enum scene
+ SCENE_MYDEV
+
+In wii_ec.c
+ Add the device definition to the ecId[] array
+ [PID_MYDEV] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "My Device", SCENE_MYDEV,
+ mydev_init, mydev_decode, mydev_msg, mydev_calib, mydev_show, mydev_key },
diff --git a/applications/plugins/wii_ec_anal/WiiEC.png b/applications/plugins/wii_ec_anal/WiiEC.png
new file mode 100644
index 000000000..6e1afcb0c
Binary files /dev/null and b/applications/plugins/wii_ec_anal/WiiEC.png differ
diff --git a/applications/plugins/wii_ec_anal/_image_tool/LICENSE b/applications/plugins/wii_ec_anal/_image_tool/LICENSE
new file mode 100644
index 000000000..95e544a06
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 BlueChip
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/applications/plugins/wii_ec_anal/_image_tool/README b/applications/plugins/wii_ec_anal/_image_tool/README
new file mode 100644
index 000000000..979605a08
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/README
@@ -0,0 +1,30 @@
+1. Prepare the image
+ a. Open your *black and white* image in GIMP
+ b. File -> Export As
+ filename: EXAMPLE.c
+ Type : "C source code"
+ [Export]
+ prefixed name: gimp_image
+ Comment :
+ [x] Use GLib types
+ [ ] <>
+ Opacity : 100%
+ [Export]
+
+2. Prepare conversion tool [stored in (eg.) /path/]
+ a. cp _convert*.* /path/
+ b. cp EXAMPLE.c /path/
+
+3. Run the conversion tool
+ a. cd /path/
+ b. ./_convert.sh EXAMPLE.c
+
+4. All being well, you will see an ascii version of your image.
+ If not, then you're gonna have to submit a bug report
+
+5. You should now have a directory called img_/
+ In that directory should be
+ img_EXAMPLE.c - The data for your new image
+ img_*.c - The data for other images
+ images.h - A header for ALL images that have been created in this directory
+ images.c - A sample FlipperZero show() function [not optimised]
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.c b/applications/plugins/wii_ec_anal/_image_tool/_convert.c
new file mode 100644
index 000000000..267985e8d
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/_convert.c
@@ -0,0 +1,138 @@
+#include
+#include
+#include
+#include
+
+int main (int argc, char* argv[])
+{
+ const unsigned char* pp = NULL;
+ uint32_t pix = 0;
+ int bit = 0;
+
+ uint8_t b = 0;
+ uint8_t bcnt = 0;
+
+ unsigned int lcnt = 0;
+ static const int lmax = 16; // max hex values per line
+
+ uint8_t* buf = NULL;
+ uint8_t* bp = NULL;
+ unsigned int blen = 0;
+
+ uint8_t* cmp = NULL;
+ uint8_t* cp = NULL;
+ unsigned int clen = 0;
+ uint8_t ctag = 0xFF;
+ uint32_t tag[256] = {0};
+ uint32_t tmax = UINT32_MAX;
+
+ unsigned int x, y, z;
+
+ const char* name = argv[1];
+ FILE* fh = fopen(argv[2], "wb");
+
+ uint32_t white = 0xFF;
+
+ int rv = 0; // assume success
+
+ // allocate buffers
+ blen = ((img.w * img.h) +0x7) >>3;
+ bp = (buf = calloc(blen +1, 1));
+ cp = (cmp = calloc(blen +4, 1));
+
+ // sanity check
+ if (!fh || !buf || !cmp) {
+ printf("! fopen() or malloc() fail.\n");
+ rv = 255;
+ goto bail;
+ }
+
+ // Find white value
+ for (x = 1; x < img.bpp; x++)
+ white = (white << 8) | 0xFF ;
+
+ // build bit pattern
+ // create the comment as we go
+ for (pp = img.b, y = 0; y < img.h; y++) {
+ fprintf(fh, "// ");
+ for (x = 0; x < img.w; x++) {
+ // read pixel
+ for (pix = 0, z = 0; z < img.bpp; pix = (pix << 8) | *pp++, z++) ;
+ // get bit and draw
+ if (pix < white) {
+ b = (b << 1) | 1;
+ fprintf(fh, "##");
+ } else {
+ b <<= 1;
+ fprintf(fh, "..");
+ }
+ // got byte
+ if ((++bcnt) == 8) {
+ *bp++ = b;
+ tag[b]++;
+ bcnt = (b = 0);
+ }
+ }
+ fprintf(fh, "\n");
+ }
+ fprintf(fh, "\n");
+ // padding
+ if (bcnt) {
+ b <<= (bcnt = 8 - bcnt);
+ *bp++ = b;
+ tag[b]++;
+ }
+ // Kill the compression
+ *bp = ~bp[-1]; // https://youtube.com/clip/Ugkx-JZIr16hETy7hz_H6yIdKPtxVe8C5w_V
+
+ // Byte run length compression
+ // Find a good tag
+ for (x = 0; tmax && (x < 256); x++) {
+ if (tag[x] < tmax) {
+ tmax = tag[x];
+ ctag = x;
+ }
+ }
+
+ // compress the data
+ for (bp = buf, x = 0; (clen < blen) && (x < blen); x++) {
+ // need at least 4 the same to be worth it
+ // must compress tag (if it occurs)
+ if ((bp[x] == bp[x+1]) && (bp[x] == bp[x+2]) && (bp[x] == bp[x+3]) || (bp[x] == ctag)) {
+ for (y = 1; (y < 255) && (bp[x] == bp[x+y]); y++) ;
+ *cp++ = ctag; // tag
+ *cp++ = y; // length
+ *cp++ = bp[x]; // byte
+ x += y -1;
+ clen += 3;
+ } else {
+ *cp++ = bp[x];
+ clen++;
+ }
+ }
+
+ // create struct
+ fprintf(fh, "#include \"images.h\"\n\n");
+ fprintf(fh, "const image_t img_%s = { %d, %d, ", name, img.w, img.h);
+
+ if (clen < blen) { // dump compressed?
+ fprintf(fh, "true, %d, 0x%02X, { // orig:%d, comp:%.2f%%\n\t",
+ clen, ctag, blen, 100.0-((clen*100.0)/blen));
+ for (x = 0; x < clen; x++)
+ if (x == clen -1) fprintf(fh, "0x%02X\n}};\n", cmp[x]) ;
+ else fprintf(fh, "0x%02X%s", cmp[x], (!((x+1)%16)) ? ",\n\t" : ", ") ;
+
+ } else { // dump UNcompressed
+ fprintf(fh, "false, %d, 0, {\n\t", blen);
+ for (x = 0; x < blen; x++)
+ if (x == blen -1) fprintf(fh, "0x%02X\n}};\n", buf[x]) ;
+ else fprintf(fh, "0x%02X%s", buf[x], (!((x+1)%16)) ? ",\n\t" : ", ") ;
+ }
+
+bail:
+ if (fh) fclose(fh) ;
+ if (buf) free(buf) ;
+ if (cmp) free(cmp) ;
+
+ return rv;
+}
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.sh b/applications/plugins/wii_ec_anal/_image_tool/_convert.sh
new file mode 100644
index 000000000..aaa7977b5
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/_convert.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+[ -z $1 ] && {
+ echo "Specify an image"
+ echo "gimp -> export -> c source file -> [x] gunit names"
+ exit 2
+}
+
+echo $*
+
+for N in $* ; do
+
+ [ ! -f $N ] && {
+ echo "!! File missing $N"
+ continue
+ }
+
+ # filename (sans extension)
+ FN=$(basename -- "$N")
+ EXT="${FN##*.}"
+ NAME="${FN%.*}"
+
+ OUTDIR=img_/
+ mkdir -p ${OUTDIR}
+
+ HDR=${OUTDIR}/images.h
+ SRC=${OUTDIR}/images.c
+
+ OUT=${OUTDIR}/img_${NAME}.c
+
+ echo -e "\n¦${N}¦ == ¦${NAME}¦ -> ¦${OUT}¦"
+
+ TESTX=test_${NAME}
+ TESTC=test_${NAME}.c
+
+ # compile name
+ CONV=${NAME}_
+
+ # clean up gimp output
+ sed -e "s/gimp_image/img/g" \
+ -e 's/guint8/unsigned char/g' \
+ -e 's/width/w/g' \
+ -e 's/height/h/g' \
+ -e 's/bytes_per_pixel/bpp/g' \
+ -e 's/pixel_data/b/g' \
+ -e 's/guint/unsigned int/g' \
+ $N \
+ | grep -v ^/ \
+ | grep -v ^$ \
+ > ${CONV}.c
+
+ # append conversion code
+ cat _convert.c >> ${CONV}.c
+
+ # compile & run converter
+ rm -f ${CONV}
+ gcc ${CONV}.c -DIMGTEST -o ${CONV}
+ ./${CONV} ${NAME} ${OUT}
+ rm -f ${CONV} ${CONV}.c
+
+ # (create &) update header
+ [[ ! -f ${HDR} ]] && cp _convert_images.h ${HDR}
+ sed -i "/ img_${NAME};/d" ${HDR}
+ sed -i "s#//\[TAG\]#//\[TAG\]\nextern const image_t img_${NAME};#" ${HDR}
+
+ # sample FZ code
+ [[ ! -f images.c ]] && cp _convert_images.c ${SRC}
+
+ # test
+ ROOT=${PWD}
+ pushd ${OUTDIR} >/dev/null
+ sed "s/zzz/${NAME}/" ${ROOT}/_convert_test.c > ${TESTC}
+ rm -f ${TESTX}
+ gcc ${TESTC} ${OUT##*/} -DIMGTEST -o ${TESTX}
+ ./${TESTX}
+ rm -f ${TESTX} ${TESTC}
+ popd >/dev/null
+
+done
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c
new file mode 100644
index 000000000..57046e9a3
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c
@@ -0,0 +1,141 @@
+#include // GUI (screen/keyboard) API
+
+#include "images.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+static Canvas* _canvas;
+static uint8_t _tlx;
+static uint8_t _tly;
+
+static uint8_t _x;
+static uint8_t _y;
+
+static const image_t* _img;
+
+static bool _blk;
+static Color _set;
+static Color _clr;
+
+//+============================================================================
+static
+void _showByteSet (const uint8_t b)
+{
+ for (uint8_t m = 0x80; m; m >>= 1) {
+ if (b & m) // plot only SET bits
+ canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
+ if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
+ }
+}
+
+//+============================================================================
+static
+void _showByteClr (const uint8_t b)
+{
+ for (uint8_t m = 0x80; m; m >>= 1) {
+ if (!(b & m)) // plot only CLR bits
+ canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
+ if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
+ }
+}
+
+//+============================================================================
+static
+void _showByteAll (const uint8_t b)
+{
+ for (uint8_t m = 0x80; m; m >>= 1) {
+ if ((!!(b & m)) ^ _blk) { // Change colour only when required
+ canvas_set_color(_canvas, ((b & m) ? _set : _clr));
+ _blk = !_blk;
+ }
+ canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
+ if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
+ }
+}
+
+//+============================================================================
+// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK
+// SHOW_XOR - same as SET_BLACK
+// SHOW_SET_WHT - plot image pixels that are SET in WHITE
+// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK
+// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE
+// SHOW_ALL - plot all images pixels as they are
+// SHOW_ALL_INV - plot all images pixels inverted
+//
+void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
+ const image_t* img, const showMode_t mode)
+{
+ void(*fnShow)(const uint8_t) = NULL;
+
+ const uint8_t* bp = img->data;
+
+ // code size optimisation
+ switch (mode & SHOW_INV_) {
+ case SHOW_NRM_:
+ _set = ColorBlack;
+ _clr = ColorWhite;
+ break;
+
+ case SHOW_INV_:
+ _set = ColorWhite;
+ _clr = ColorBlack;
+ break;
+
+ case SHOW_BLK_:
+ canvas_set_color(canvas, ColorBlack);
+ break;
+
+ case SHOW_WHT_:
+ canvas_set_color(canvas, ColorWhite);
+ break;
+
+ }
+ switch (mode & SHOW_INV_) {
+ case SHOW_NRM_:
+ case SHOW_INV_:
+ fnShow = _showByteAll;
+ canvas_set_color(canvas, ColorWhite);
+ _blk = 0;
+ break;
+
+ case SHOW_BLK_:
+ case SHOW_WHT_:
+ switch (mode & SHOW_ALL_) {
+ case SHOW_SET_:
+ fnShow = _showByteSet;
+ break;
+ case SHOW_CLR_:
+ fnShow = _showByteClr;
+ break;
+ }
+ break;
+ }
+ furi_check(fnShow);
+
+ // I want nested functions!
+ _canvas = canvas;
+ _img = img;
+ _tlx = tlx;
+ _tly = tly;
+ _x = 0;
+ _y = 0;
+
+ // Compressed
+ if (img->c) {
+ for (unsigned int i = 0; i < img->len; i++, bp++) {
+ // Compressed data? {tag, length, value}
+ if (*bp == img->tag) {
+ for (uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]) ;
+ bp += 3 -1;
+ i += 3 -1;
+
+ // Uncompressed byte
+ } else {
+ fnShow(*bp);
+ }
+ }
+
+ // Not compressed
+ } else {
+ for (unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp) ;
+ }
+}
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h
new file mode 100644
index 000000000..bfc44568e
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h
@@ -0,0 +1,53 @@
+#ifndef IMAGES_H_
+#define IMAGES_H_
+
+#include
+#include
+
+//----------------------------------------------------------------------------- ----------------------------------------
+typedef
+ enum showMode {
+ // {INV:--:WHT:BLK::--:--:CLR:SET}
+ SHOW_SET_ = 0x01,
+ SHOW_CLR_ = 0x02,
+ SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_,
+
+ SHOW_BLK_ = 0x10,
+ SHOW_WHT_ = 0x20,
+ SHOW_NRM_ = 0x00,
+ SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_,
+
+ SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_,
+ SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_,
+
+ SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_,
+ SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_,
+
+ SHOW_ALL = SHOW_ALL_ | SHOW_NRM_,
+ SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_,
+ }
+showMode_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+typedef
+ struct image {
+ uint8_t w; // width
+ uint8_t h; // height
+ bool c; // compressed?
+ uint16_t len; // image data length
+ uint8_t tag; // rle tag
+ uint8_t data[]; // image data
+ }
+image_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+//[TAG]
+
+//----------------------------------------------------------------------------- ----------------------------------------
+#ifndef IMGTEST
+# include
+ void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
+ const image_t* img, const showMode_t mode) ;
+#endif
+
+#endif //IMAGES_H_
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c b/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c
new file mode 100644
index 000000000..4bdb531d5
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c
@@ -0,0 +1,59 @@
+#include
+#include
+
+#include "images.h"
+
+//-----------------------------------------------------------------------------
+// This will be the plot function out of your graphics library
+//
+#define PLOT(x,y,c) do { \
+ printf("%s", (c ? "#" : ".")); \
+ if (x == img->w -1) printf("\n") ; \
+}while(0)
+
+//+============================================================================
+// The pain we endure to avoid code duplication cleanly
+//
+#define PLOTBYTE(b) do { \
+ for (uint8_t m = 0x80; m; m>>=1) { \
+ PLOT(x,y, (b & m)); \
+ if ( ((++x) == img->w) && !(x = 0) && ((++y) == img->h) ) break ; \
+ } \
+}while(0)
+
+void show (const image_t* img)
+{
+ // Some variables
+ const uint8_t* bp = img->data;
+ unsigned int x = 0;
+ unsigned int y = 0;
+
+ // Compressed
+ if (img->c) {
+ for (unsigned int i = 0; i < img->len; i++, bp++) {
+ // Compressed data? {tag, length, value}
+ if (*bp == img->tag) {
+ for (uint16_t c = 0; c < bp[1]; c++) PLOTBYTE(bp[2]) ;
+ bp += 3 -1;
+ i += 3 -1;
+
+ // Uncompressed byte
+ } else {
+ PLOTBYTE(*bp);
+ }
+ }
+
+ // Not compressed
+ } else {
+ for (unsigned int i = 0; i < img->len; i++, bp++) PLOTBYTE(*bp) ;
+ }
+}
+
+#undef PLOTBYTE
+
+//+============================================================================
+int main (void)
+{
+ show(&img_zzz);
+ return 0;
+}
diff --git a/applications/plugins/wii_ec_anal/_images/CLASSIC.png b/applications/plugins/wii_ec_anal/_images/CLASSIC.png
new file mode 100644
index 000000000..aa5318b33
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/CLASSIC.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/CLASSIC_N.png b/applications/plugins/wii_ec_anal/_images/CLASSIC_N.png
new file mode 100644
index 000000000..24f4ac225
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/CLASSIC_N.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/DEBUG.png b/applications/plugins/wii_ec_anal/_images/DEBUG.png
new file mode 100644
index 000000000..bca35c693
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/DEBUG.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/DUMP.png b/applications/plugins/wii_ec_anal/_images/DUMP.png
new file mode 100644
index 000000000..dc9328aab
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/DUMP.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf
new file mode 100644
index 000000000..67f70139e
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/RIP.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/RIP.xcf
new file mode 100644
index 000000000..0058fe9c8
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/RIP.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/Wiring.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/Wiring.xcf
new file mode 100644
index 000000000..aa8078db8
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/Wiring.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/classic.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/classic.xcf
new file mode 100644
index 000000000..6fd152675
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/classic.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/csLogo.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/csLogo.xcf
new file mode 100644
index 000000000..f4e33844a
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/csLogo.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/fonts.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/fonts.xcf
new file mode 100644
index 000000000..d05d03fc7
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/fonts.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/frame.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/frame.xcf
new file mode 100644
index 000000000..31705cf72
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/frame.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/port.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/port.xcf
new file mode 100644
index 000000000..10fcd2de2
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/port.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/social.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/social.xcf
new file mode 100644
index 000000000..377eaa63b
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/social.xcf differ
diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK.png
new file mode 100644
index 000000000..bc31ae386
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_acc.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_acc.png
new file mode 100644
index 000000000..895c85e4c
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_acc.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_anal.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_anal.png
new file mode 100644
index 000000000..e821d7ee2
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_anal.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.gif b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.gif
new file mode 100644
index 000000000..72d807a54
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.gif differ
diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.png
new file mode 100644
index 000000000..f9d34bb93
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/Nunchucky.png b/applications/plugins/wii_ec_anal/_images/Nunchucky.png
new file mode 100644
index 000000000..3af395da6
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/Nunchucky.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/RIP.png b/applications/plugins/wii_ec_anal/_images/RIP.png
new file mode 100644
index 000000000..0acfe0c00
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/RIP.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/SPLASH.png b/applications/plugins/wii_ec_anal/_images/SPLASH.png
new file mode 100644
index 000000000..a5c3f093a
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/SPLASH.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/WAIT.png b/applications/plugins/wii_ec_anal/_images/WAIT.png
new file mode 100644
index 000000000..776edc3f1
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/WAIT.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/WiiChuck.png b/applications/plugins/wii_ec_anal/_images/WiiChuck.png
new file mode 100644
index 000000000..532ce3096
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/WiiChuck.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/Wiring.png b/applications/plugins/wii_ec_anal/_images/Wiring.png
new file mode 100644
index 000000000..300c07ee4
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/Wiring.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/plug.png b/applications/plugins/wii_ec_anal/_images/plug.png
new file mode 100644
index 000000000..c418f43b1
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/plug.png differ
diff --git a/applications/plugins/wii_ec_anal/_images/social.png b/applications/plugins/wii_ec_anal/_images/social.png
new file mode 100644
index 000000000..1d3eddcc5
Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/social.png differ
diff --git a/applications/plugins/wii_ec_anal/application.fam b/applications/plugins/wii_ec_anal/application.fam
new file mode 100644
index 000000000..ac11d260e
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/application.fam
@@ -0,0 +1,36 @@
+# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
+
+App(
+ # --- App Info
+ appid="wii_ec_anal",
+ name="Wii EC Analyser",
+
+ # --- Entry point
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="wii_ec_anal",
+
+ # --- Interaction
+ cdefines=["APP_WII_EC_ANAL"],
+ requires=[
+ "gui",
+ ],
+
+# conflicts="",
+# sdk_headers="",
+
+ # --- Run-time info
+ stack_size=2 * 1024,
+ order=20,
+
+ # --- FAP details
+ sources=["wii_*.c", "gfx/*.c"],
+
+# fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/",
+# fap_author="BlueChip",
+
+# fap_description="Wii Extension Controller Protocol Analyser",
+# fap_version=(1,0),
+
+ fap_icon="WiiEC.png",
+ fap_category="Misc",
+)
diff --git a/applications/plugins/wii_ec_anal/bc_logging.h b/applications/plugins/wii_ec_anal/bc_logging.h
new file mode 100644
index 000000000..d9bb48c92
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/bc_logging.h
@@ -0,0 +1,70 @@
+#ifndef BC_LOGGING_H_
+#define BC_LOGGING_H_
+
+#include
+#include "err.h" // appName
+
+//! WARNING: There is a bug in Furi such that if you crank LOG_LEVEL up to 6=TRACE
+//! AND you have menu->settings->system->logLevel = trace
+//! THEN this program will cause the FZ to crash when the plugin exits!
+#define LOG_LEVEL 4
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time
+// ... LOG_LEVEL lets you limit it at COMPILE-time
+//
+// FURI logging has 6 levels (numbered 1 thru 6}
+// 1. None
+// 2. Errors FURI_LOG_E
+// 3. Warnings FURI_LOG_W
+// 4. Information FURI_LOG_I
+// 5. Debug FURI_LOG_D
+// 6. Trace FURI_LOG_T
+//
+// --> furi/core/log.h
+//
+
+// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time
+// This lets you limit it at COMPILE-time
+#ifndef LOG_LEVEL
+# define LOG_LEVEL 6 // default = full logging
+#endif
+
+#if (LOG_LEVEL < 2)
+# undef FURI_LOG_E
+# define FURI_LOG_E(tag, fmt, ...)
+#endif
+
+#if (LOG_LEVEL < 3)
+# undef FURI_LOG_W
+# define FURI_LOG_W(tag, fmt, ...)
+#endif
+
+#if (LOG_LEVEL < 4)
+# undef FURI_LOG_I
+# define FURI_LOG_I(tag, fmt, ...)
+#endif
+
+#if (LOG_LEVEL < 5)
+# undef FURI_LOG_D
+# define FURI_LOG_D(tag, fmt, ...)
+#endif
+
+#if (LOG_LEVEL < 6)
+# undef FURI_LOG_T
+# define FURI_LOG_T(tag, fmt, ...)
+#endif
+
+//----------------------------------------------------------
+// Logging helper macros
+//
+#define ERROR(fmt, ...) FURI_LOG_E(appName, fmt __VA_OPT__(,) __VA_ARGS__)
+#define WARN(fmt, ...) FURI_LOG_W(appName, fmt __VA_OPT__(,) __VA_ARGS__)
+#define INFO(fmt, ...) FURI_LOG_I(appName, fmt __VA_OPT__(,) __VA_ARGS__)
+#define DEBUG(fmt, ...) FURI_LOG_D(appName, fmt __VA_OPT__(,) __VA_ARGS__)
+#define TRACE(fmt, ...) FURI_LOG_T(appName, fmt __VA_OPT__(,) __VA_ARGS__)
+
+#define ENTER TRACE("(+) %s", __func__)
+#define LEAVE TRACE("(-) %s", __func__)
+
+#endif //BC_LOGGING_H_
diff --git a/applications/plugins/wii_ec_anal/err.h b/applications/plugins/wii_ec_anal/err.h
new file mode 100644
index 000000000..9398a3fb8
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/err.h
@@ -0,0 +1,69 @@
+// Avoid circular/nested/mulitple inclusion
+#ifndef ERR_H_
+#define ERR_H_
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Application name
+//
+static const char* const appName = "Wii_i2c"; //$ Name used in log files
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Error codes and messages
+//
+
+// You should only ever (need to) edit this list
+// ...Watch out for extraneous whitespace after the terminating backslashes
+#define FOREACH_ES(esPrial) \
+ /* The first line MUST define 'ERR_OK = 0' */ \
+ esPrial( 0, ERR_OK , "OK (no error)") \
+\
+ esPrial( 1, ERR_MALLOC_QUEUE , "malloc() fail - queue") \
+ esPrial( 2, ERR_MALLOC_STATE , "malloc() fail - state") \
+ esPrial( 3, ERR_MALLOC_TEXT , "malloc() fail - text") \
+ esPrial( 4, ERR_MALLOC_VIEW , "malloc() fail - viewport") \
+ esPrial( 5, ERR_NO_MUTEX , "Cannot create mutex") \
+ esPrial( 6, ERR_NO_GUI , "Cannot open GUI") \
+ esPrial( 7, ERR_NO_TIMER , "Cannot create timer") \
+ esPrial( 8, ERR_NO_NOTIFY , "Cannot acquire notifications handle") \
+\
+ esPrial(10, ERR_MUTEX_BLOCK , "Mutex block failed") \
+ esPrial(11, ERR_MUTEX_RELEASE , "Mutex release failed") \
+\
+ esPrial(20, ERR_QUEUE_RTOS , "queue - Undefined RTOS error") \
+ esPrial(21, DEBUG_QUEUE_TIMEOUT, "queue - Timeout") \
+ esPrial(22, ERR_QUEUE_RESOURCE , "queue - Resource not available") \
+ esPrial(23, ERR_QUEUE_BADPRM , "queue - Bad parameter") \
+ esPrial(24, ERR_QUEUE_NOMEM , "queue - Out of memory") \
+ esPrial(25, ERR_QUEUE_ISR , "queue - Banned in ISR") \
+ esPrial(26, ERR_QUEUE_UNK , "queue - Unknown") \
+\
+ esPrial(30, WARN_SCAN_START , "Scan - Already started") \
+ esPrial(31, WARN_SCAN_STOP , "Scan - Already stopped") \
+ esPrial(32, ERR_TIMER_START , "Scan - Cannot start timer") \
+ esPrial(33, ERR_TIMER_STOP , "Scan - Cannot stop timer") \
+//[EOT]
+
+// Declare list extraction macros
+#define ES_ENUM(num, ename, string) ename = num,
+#define ES_STRING(num, ename, string) string"\r\n",
+
+// Build the enum
+typedef
+ enum err { FOREACH_ES(ES_ENUM) }
+err_t ;
+
+// You need to '#define ERR_C_' in precisely ONE source file
+#ifdef ERR_C_
+ // Build the string list
+ const char* const wii_errs[] = { FOREACH_ES(ES_STRING) };
+#else
+ // Give access to string list
+ extern const char* const wii_errs[];
+#endif
+
+// This is a header file, clean up
+#undef ES_ENUM
+#undef ES_STRING
+#undef FOREACH_ES
+
+#endif // ERR_H_
diff --git a/applications/plugins/wii_ec_anal/gfx/images.c b/applications/plugins/wii_ec_anal/gfx/images.c
new file mode 100644
index 000000000..57046e9a3
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/images.c
@@ -0,0 +1,141 @@
+#include // GUI (screen/keyboard) API
+
+#include "images.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+static Canvas* _canvas;
+static uint8_t _tlx;
+static uint8_t _tly;
+
+static uint8_t _x;
+static uint8_t _y;
+
+static const image_t* _img;
+
+static bool _blk;
+static Color _set;
+static Color _clr;
+
+//+============================================================================
+static
+void _showByteSet (const uint8_t b)
+{
+ for (uint8_t m = 0x80; m; m >>= 1) {
+ if (b & m) // plot only SET bits
+ canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
+ if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
+ }
+}
+
+//+============================================================================
+static
+void _showByteClr (const uint8_t b)
+{
+ for (uint8_t m = 0x80; m; m >>= 1) {
+ if (!(b & m)) // plot only CLR bits
+ canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
+ if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
+ }
+}
+
+//+============================================================================
+static
+void _showByteAll (const uint8_t b)
+{
+ for (uint8_t m = 0x80; m; m >>= 1) {
+ if ((!!(b & m)) ^ _blk) { // Change colour only when required
+ canvas_set_color(_canvas, ((b & m) ? _set : _clr));
+ _blk = !_blk;
+ }
+ canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ;
+ if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ;
+ }
+}
+
+//+============================================================================
+// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK
+// SHOW_XOR - same as SET_BLACK
+// SHOW_SET_WHT - plot image pixels that are SET in WHITE
+// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK
+// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE
+// SHOW_ALL - plot all images pixels as they are
+// SHOW_ALL_INV - plot all images pixels inverted
+//
+void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
+ const image_t* img, const showMode_t mode)
+{
+ void(*fnShow)(const uint8_t) = NULL;
+
+ const uint8_t* bp = img->data;
+
+ // code size optimisation
+ switch (mode & SHOW_INV_) {
+ case SHOW_NRM_:
+ _set = ColorBlack;
+ _clr = ColorWhite;
+ break;
+
+ case SHOW_INV_:
+ _set = ColorWhite;
+ _clr = ColorBlack;
+ break;
+
+ case SHOW_BLK_:
+ canvas_set_color(canvas, ColorBlack);
+ break;
+
+ case SHOW_WHT_:
+ canvas_set_color(canvas, ColorWhite);
+ break;
+
+ }
+ switch (mode & SHOW_INV_) {
+ case SHOW_NRM_:
+ case SHOW_INV_:
+ fnShow = _showByteAll;
+ canvas_set_color(canvas, ColorWhite);
+ _blk = 0;
+ break;
+
+ case SHOW_BLK_:
+ case SHOW_WHT_:
+ switch (mode & SHOW_ALL_) {
+ case SHOW_SET_:
+ fnShow = _showByteSet;
+ break;
+ case SHOW_CLR_:
+ fnShow = _showByteClr;
+ break;
+ }
+ break;
+ }
+ furi_check(fnShow);
+
+ // I want nested functions!
+ _canvas = canvas;
+ _img = img;
+ _tlx = tlx;
+ _tly = tly;
+ _x = 0;
+ _y = 0;
+
+ // Compressed
+ if (img->c) {
+ for (unsigned int i = 0; i < img->len; i++, bp++) {
+ // Compressed data? {tag, length, value}
+ if (*bp == img->tag) {
+ for (uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]) ;
+ bp += 3 -1;
+ i += 3 -1;
+
+ // Uncompressed byte
+ } else {
+ fnShow(*bp);
+ }
+ }
+
+ // Not compressed
+ } else {
+ for (unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp) ;
+ }
+}
diff --git a/applications/plugins/wii_ec_anal/gfx/images.h b/applications/plugins/wii_ec_anal/gfx/images.h
new file mode 100644
index 000000000..87f2b89b7
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/images.h
@@ -0,0 +1,134 @@
+#ifndef IMAGES_H_
+#define IMAGES_H_
+
+#include
+#include
+
+//----------------------------------------------------------------------------- ----------------------------------------
+typedef
+ enum showMode {
+ // {INV:--:WHT:BLK::--:--:CLR:SET}
+ SHOW_SET_ = 0x01,
+ SHOW_CLR_ = 0x02,
+ SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_,
+
+ SHOW_BLK_ = 0x10,
+ SHOW_WHT_ = 0x20,
+ SHOW_NRM_ = 0x00,
+ SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_,
+
+ SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_,
+ SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_,
+
+ SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_,
+ SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_,
+
+ SHOW_ALL = SHOW_ALL_ | SHOW_NRM_,
+ SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_,
+ }
+showMode_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+typedef
+ struct image {
+ uint8_t w; // width
+ uint8_t h; // height
+ bool c; // compressed?
+ uint16_t len; // image data length
+ uint8_t tag; // rle tag
+ uint8_t data[]; // image data
+ }
+image_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+//[TAG]
+extern const image_t img_csLogo_Small;
+extern const image_t img_3x5_v;
+extern const image_t img_3x5_9;
+extern const image_t img_3x5_8;
+extern const image_t img_3x5_7;
+extern const image_t img_3x5_6;
+extern const image_t img_3x5_5;
+extern const image_t img_3x5_4;
+extern const image_t img_3x5_3;
+extern const image_t img_3x5_2;
+extern const image_t img_3x5_1;
+extern const image_t img_3x5_0;
+extern const image_t img_key_Ui;
+extern const image_t img_key_OKi;
+extern const image_t img_RIP;
+extern const image_t img_cc_trg_R4;
+extern const image_t img_cc_trg_R3;
+extern const image_t img_cc_trg_R2;
+extern const image_t img_cc_trg_R1;
+extern const image_t img_cc_trg_L4;
+extern const image_t img_cc_trg_L3;
+extern const image_t img_cc_trg_L2;
+extern const image_t img_cc_trg_L1;
+extern const image_t img_cc_Joy;
+extern const image_t img_cc_Main;
+extern const image_t img_cc_Cable;
+extern const image_t img_key_Back;
+extern const image_t img_key_OK;
+extern const image_t img_6x8_Z;
+extern const image_t img_6x8_Y;
+extern const image_t img_6x8_X;
+extern const image_t img_key_U;
+extern const image_t img_key_D;
+extern const image_t img_csLogo_FULL;
+extern const image_t img_6x8_7;
+extern const image_t img_key_R;
+extern const image_t img_key_L;
+extern const image_t img_5x7_7;
+extern const image_t img_5x7_F;
+extern const image_t img_5x7_E;
+extern const image_t img_5x7_D;
+extern const image_t img_5x7_C;
+extern const image_t img_5x7_B;
+extern const image_t img_5x7_A;
+extern const image_t img_5x7_9;
+extern const image_t img_5x7_8;
+extern const image_t img_5x7_6;
+extern const image_t img_5x7_5;
+extern const image_t img_5x7_4;
+extern const image_t img_5x7_3;
+extern const image_t img_5x7_2;
+extern const image_t img_5x7_1;
+extern const image_t img_5x7_0;
+extern const image_t img_6x8_v;
+extern const image_t img_6x8_n;
+extern const image_t img_6x8_G;
+extern const image_t img_6x8_F;
+extern const image_t img_6x8_E;
+extern const image_t img_6x8_d;
+extern const image_t img_6x8_C;
+extern const image_t img_6x8_B;
+extern const image_t img_6x8_A;
+extern const image_t img_6x8_9;
+extern const image_t img_6x8_8;
+extern const image_t img_6x8_6;
+extern const image_t img_6x8_5;
+extern const image_t img_6x8_4;
+extern const image_t img_6x8_3;
+extern const image_t img_6x8_2;
+extern const image_t img_6x8_1;
+extern const image_t img_6x8_0;
+extern const image_t img_ecp_SDA;
+extern const image_t img_ecp_SCL;
+extern const image_t img_ecp_port;
+extern const image_t img_cc_pad_UD1;
+extern const image_t img_cc_pad_LR1;
+extern const image_t img_cc_btn_Y1;
+extern const image_t img_cc_btn_X1;
+extern const image_t img_cc_btn_B1;
+extern const image_t img_cc_btn_A1;
+extern const image_t img_6x8_D;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+#ifndef IMGTEST
+# include
+ void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly,
+ const image_t* img, const showMode_t mode) ;
+#endif
+
+#endif //IMAGES_H_
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c
new file mode 100644
index 000000000..975d98d35
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c
@@ -0,0 +1,11 @@
+// ######
+// ##..##
+// ##..##
+// ##..##
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_0 = { 3, 5, false, 2, 0, {
+ 0xF6, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c
new file mode 100644
index 000000000..0d9dc3fe4
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c
@@ -0,0 +1,11 @@
+// ####..
+// ..##..
+// ..##..
+// ..##..
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_1 = { 3, 5, false, 2, 0, {
+ 0xC9, 0x2E
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c
new file mode 100644
index 000000000..d98bf4e93
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c
@@ -0,0 +1,11 @@
+// ######
+// ....##
+// ######
+// ##....
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_2 = { 3, 5, false, 2, 0, {
+ 0xE7, 0xCE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c
new file mode 100644
index 000000000..8d08ed1b6
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c
@@ -0,0 +1,11 @@
+// ######
+// ....##
+// ..####
+// ....##
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_3 = { 3, 5, false, 2, 0, {
+ 0xE5, 0x9E
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c
new file mode 100644
index 000000000..795e9b76f
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c
@@ -0,0 +1,11 @@
+// ##....
+// ##..##
+// ######
+// ....##
+// ....##
+
+#include "images.h"
+
+const image_t img_3x5_4 = { 3, 5, false, 2, 0, {
+ 0x97, 0x92
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c
new file mode 100644
index 000000000..377853507
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c
@@ -0,0 +1,11 @@
+// ######
+// ##....
+// ######
+// ....##
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_5 = { 3, 5, false, 2, 0, {
+ 0xF3, 0x9E
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c
new file mode 100644
index 000000000..d3af64071
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c
@@ -0,0 +1,11 @@
+// ####..
+// ##....
+// ######
+// ##..##
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_6 = { 3, 5, false, 2, 0, {
+ 0xD3, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c
new file mode 100644
index 000000000..2c3b1e0b9
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c
@@ -0,0 +1,11 @@
+// ######
+// ....##
+// ..##..
+// ..##..
+// ..##..
+
+#include "images.h"
+
+const image_t img_3x5_7 = { 3, 5, false, 2, 0, {
+ 0xE5, 0x24
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c
new file mode 100644
index 000000000..5cb6d3354
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c
@@ -0,0 +1,11 @@
+// ######
+// ##..##
+// ######
+// ##..##
+// ######
+
+#include "images.h"
+
+const image_t img_3x5_8 = { 3, 5, false, 2, 0, {
+ 0xF7, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c
new file mode 100644
index 000000000..ee5e82b87
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c
@@ -0,0 +1,11 @@
+// ######
+// ##..##
+// ######
+// ....##
+// ..####
+
+#include "images.h"
+
+const image_t img_3x5_9 = { 3, 5, false, 2, 0, {
+ 0xF7, 0x96
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c
new file mode 100644
index 000000000..dcf3f631d
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c
@@ -0,0 +1,11 @@
+// ......
+// ......
+// ##..##
+// ##..##
+// ..##..
+
+#include "images.h"
+
+const image_t img_3x5_v = { 3, 5, false, 2, 0, {
+ 0x02, 0xD4
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c
new file mode 100644
index 000000000..c59852f19
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ##....####
+// ##..##..##
+// ####....##
+// ##......##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_0 = { 5, 7, false, 5, 0, {
+ 0x74, 0x67, 0x5C, 0xC5, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c
new file mode 100644
index 000000000..4bd08f89c
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c
@@ -0,0 +1,13 @@
+// ..####....
+// ##..##....
+// ....##....
+// ....##....
+// ....##....
+// ....##....
+// ##########
+
+#include "images.h"
+
+const image_t img_5x7_1 = { 5, 7, false, 5, 0, {
+ 0x65, 0x08, 0x42, 0x13, 0xE0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c
new file mode 100644
index 000000000..1270393f7
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ........##
+// ......##..
+// ....##....
+// ..##......
+// ##########
+
+#include "images.h"
+
+const image_t img_5x7_2 = { 5, 7, false, 5, 0, {
+ 0x74, 0x42, 0x22, 0x23, 0xE0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c
new file mode 100644
index 000000000..e26bac523
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ........##
+// ....####..
+// ........##
+// ##......##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_3 = { 5, 7, false, 5, 0, {
+ 0x74, 0x42, 0x60, 0xC5, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c
new file mode 100644
index 000000000..e0dc5687f
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c
@@ -0,0 +1,13 @@
+// ##........
+// ##........
+// ##....##..
+// ##....##..
+// ##########
+// ......##..
+// ......##..
+
+#include "images.h"
+
+const image_t img_5x7_4 = { 5, 7, false, 5, 0, {
+ 0x84, 0x25, 0x2F, 0x88, 0x40
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c
new file mode 100644
index 000000000..81747376f
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c
@@ -0,0 +1,13 @@
+// ##########
+// ##........
+// ##........
+// ########..
+// ........##
+// ........##
+// ########..
+
+#include "images.h"
+
+const image_t img_5x7_5 = { 5, 7, false, 5, 0, {
+ 0xFC, 0x21, 0xE0, 0x87, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c
new file mode 100644
index 000000000..455c874dc
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##........
+// ##........
+// ########..
+// ##......##
+// ##......##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_6 = { 5, 7, false, 5, 0, {
+ 0x74, 0x21, 0xE8, 0xC5, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c
new file mode 100644
index 000000000..73e813a21
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c
@@ -0,0 +1,13 @@
+// ##########
+// ........##
+// ......##..
+// ......##..
+// ....##....
+// ....##....
+// ....##....
+
+#include "images.h"
+
+const image_t img_5x7_7 = { 5, 7, false, 5, 0, {
+ 0xF8, 0x44, 0x22, 0x10, 0x80
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c
new file mode 100644
index 000000000..0f04a48bf
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ##......##
+// ..######..
+// ##......##
+// ##......##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_8 = { 5, 7, false, 5, 0, {
+ 0x74, 0x62, 0xE8, 0xC5, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c
new file mode 100644
index 000000000..2b1e978c6
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ##......##
+// ..########
+// ........##
+// ........##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_9 = { 5, 7, false, 5, 0, {
+ 0x74, 0x62, 0xF0, 0x85, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c
new file mode 100644
index 000000000..a6b049f01
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ##......##
+// ##########
+// ##......##
+// ##......##
+// ##......##
+
+#include "images.h"
+
+const image_t img_5x7_A = { 5, 7, false, 5, 0, {
+ 0x74, 0x63, 0xF8, 0xC6, 0x20
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c
new file mode 100644
index 000000000..06b36599c
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c
@@ -0,0 +1,13 @@
+// ########..
+// ##......##
+// ##......##
+// ##..####..
+// ##......##
+// ##......##
+// ########..
+
+#include "images.h"
+
+const image_t img_5x7_B = { 5, 7, false, 5, 0, {
+ 0xF4, 0x63, 0x68, 0xC7, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c
new file mode 100644
index 000000000..c058d09a9
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##......##
+// ##........
+// ##........
+// ##........
+// ##......##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_C = { 5, 7, false, 5, 0, {
+ 0x74, 0x61, 0x08, 0x45, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c
new file mode 100644
index 000000000..3425e3648
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c
@@ -0,0 +1,13 @@
+// ..######..
+// ##..##..##
+// ....##..##
+// ....##..##
+// ....##..##
+// ##..##..##
+// ..######..
+
+#include "images.h"
+
+const image_t img_5x7_D = { 5, 7, false, 5, 0, {
+ 0x75, 0x4A, 0x52, 0xD5, 0xC0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c
new file mode 100644
index 000000000..c7bbc301a
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c
@@ -0,0 +1,13 @@
+// ##########
+// ##........
+// ##........
+// ######....
+// ##........
+// ##........
+// ##########
+
+#include "images.h"
+
+const image_t img_5x7_E = { 5, 7, false, 5, 0, {
+ 0xFC, 0x21, 0xC8, 0x43, 0xE0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c
new file mode 100644
index 000000000..440c37eae
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c
@@ -0,0 +1,13 @@
+// ##########
+// ##........
+// ##........
+// ######....
+// ##........
+// ##........
+// ##........
+
+#include "images.h"
+
+const image_t img_5x7_F = { 5, 7, false, 5, 0, {
+ 0xFC, 0x21, 0xC8, 0x42, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c
new file mode 100644
index 000000000..b8b4c7d9a
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c
@@ -0,0 +1,14 @@
+// ..########..
+// ############
+// ####....####
+// ####....####
+// ####....####
+// ####....####
+// ############
+// ..########..
+
+#include "images.h"
+
+const image_t img_6x8_0 = { 6, 8, false, 6, 0, {
+ 0x7B, 0xFC, 0xF3, 0xCF, 0x3F, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c
new file mode 100644
index 000000000..91e2b2cfa
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c
@@ -0,0 +1,14 @@
+// ..######....
+// ########....
+// ....####....
+// ....####....
+// ....####....
+// ....####....
+// ############
+// ############
+
+#include "images.h"
+
+const image_t img_6x8_1 = { 6, 8, false, 6, 0, {
+ 0x73, 0xC3, 0x0C, 0x30, 0xCF, 0xFF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c
new file mode 100644
index 000000000..7d24c64d6
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c
@@ -0,0 +1,14 @@
+// ..########..
+// ############
+// ........####
+// ......######
+// ....####....
+// ..####......
+// ############
+// ############
+
+#include "images.h"
+
+const image_t img_6x8_2 = { 6, 8, false, 6, 0, {
+ 0x7B, 0xF0, 0xC7, 0x31, 0x8F, 0xFF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c
new file mode 100644
index 000000000..3a8f9f211
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c
@@ -0,0 +1,14 @@
+// ..########..
+// ############
+// ........####
+// ....########
+// ....########
+// ........####
+// ############
+// ..########..
+
+#include "images.h"
+
+const image_t img_6x8_3 = { 6, 8, false, 6, 0, {
+ 0x7B, 0xF0, 0xCF, 0x3C, 0x3F, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c
new file mode 100644
index 000000000..c5ae9efef
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c
@@ -0,0 +1,14 @@
+// ####........
+// ####........
+// ####..####..
+// ####..####..
+// ############
+// ############
+// ......####..
+// ......####..
+
+#include "images.h"
+
+const image_t img_6x8_4 = { 6, 8, false, 6, 0, {
+ 0xC3, 0x0D, 0xB6, 0xFF, 0xF1, 0x86
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c
new file mode 100644
index 000000000..787e39ea6
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c
@@ -0,0 +1,14 @@
+// ############
+// ############
+// ####........
+// ##########..
+// ############
+// ........####
+// ############
+// ##########..
+
+#include "images.h"
+
+const image_t img_6x8_5 = { 6, 8, false, 6, 0, {
+ 0xFF, 0xFC, 0x3E, 0xFC, 0x3F, 0xFE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c
new file mode 100644
index 000000000..8f07f1bfc
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c
@@ -0,0 +1,14 @@
+// ..########..
+// ##########..
+// ####........
+// ##########..
+// ############
+// ####....####
+// ############
+// ..########..
+
+#include "images.h"
+
+const image_t img_6x8_6 = { 6, 8, false, 6, 0, {
+ 0x7B, 0xEC, 0x3E, 0xFF, 0x3F, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c
new file mode 100644
index 000000000..cad50c65d
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c
@@ -0,0 +1,14 @@
+// ############
+// ############
+// ........####
+// ......####..
+// ......####..
+// ....####....
+// ....####....
+// ....####....
+
+#include "images.h"
+
+const image_t img_6x8_7 = { 6, 8, false, 6, 0, {
+ 0xFF, 0xF0, 0xC6, 0x18, 0xC3, 0x0C
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c
new file mode 100644
index 000000000..a38b2110d
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c
@@ -0,0 +1,14 @@
+// ..########..
+// ############
+// ####....####
+// ..########..
+// ############
+// ####....####
+// ############
+// ..########..
+
+#include "images.h"
+
+const image_t img_6x8_8 = { 6, 8, false, 6, 0, {
+ 0x7B, 0xFC, 0xDE, 0xFF, 0x3F, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c
new file mode 100644
index 000000000..b740c7f90
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c
@@ -0,0 +1,14 @@
+// ..########..
+// ############
+// ####....####
+// ############
+// ..##########
+// ........####
+// ..##########
+// ..########..
+
+#include "images.h"
+
+const image_t img_6x8_9 = { 6, 8, false, 6, 0, {
+ 0x7B, 0xFC, 0xFF, 0x7C, 0x37, 0xDE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c
new file mode 100644
index 000000000..fa3aed598
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c
@@ -0,0 +1,14 @@
+// ..########..
+// ############
+// ####....####
+// ####....####
+// ############
+// ############
+// ####....####
+// ####....####
+
+#include "images.h"
+
+const image_t img_6x8_A = { 6, 8, false, 6, 0, {
+ 0x7B, 0xFC, 0xF3, 0xFF, 0xFC, 0xF3
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c
new file mode 100644
index 000000000..14c1e28c6
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c
@@ -0,0 +1,14 @@
+// ##########..
+// ############
+// ####....####
+// ##########..
+// ##########..
+// ####....####
+// ############
+// ##########..
+
+#include "images.h"
+
+const image_t img_6x8_B = { 6, 8, false, 6, 0, {
+ 0xFB, 0xFC, 0xFE, 0xFB, 0x3F, 0xFE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c
new file mode 100644
index 000000000..6d8f7aa32
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c
@@ -0,0 +1,14 @@
+// ..##########
+// ############
+// ####........
+// ####........
+// ####........
+// ####........
+// ############
+// ..##########
+
+#include "images.h"
+
+const image_t img_6x8_C = { 6, 8, false, 6, 0, {
+ 0x7F, 0xFC, 0x30, 0xC3, 0x0F, 0xDF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c
new file mode 100644
index 000000000..474e4a235
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c
@@ -0,0 +1,14 @@
+// ##########..
+// ############
+// ..####..####
+// ..####..####
+// ..####..####
+// ..####..####
+// ############
+// ##########..
+
+#include "images.h"
+
+const image_t img_6x8_D = { 6, 8, false, 6, 0, {
+ 0xFB, 0xF6, 0xDB, 0x6D, 0xBF, 0xFE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c
new file mode 100644
index 000000000..00f2cb559
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c
@@ -0,0 +1,14 @@
+// ############
+// ############
+// ####........
+// ########....
+// ########....
+// ####........
+// ############
+// ############
+
+#include "images.h"
+
+const image_t img_6x8_E = { 6, 8, false, 6, 0, {
+ 0xFF, 0xFC, 0x3C, 0xF3, 0x0F, 0xFF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c
new file mode 100644
index 000000000..8958a0419
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c
@@ -0,0 +1,14 @@
+// ############
+// ############
+// ####........
+// ########....
+// ########....
+// ####........
+// ####........
+// ####........
+
+#include "images.h"
+
+const image_t img_6x8_F = { 6, 8, false, 6, 0, {
+ 0xFF, 0xFC, 0x3C, 0xF3, 0x0C, 0x30
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c
new file mode 100644
index 000000000..f5e8f03f4
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c
@@ -0,0 +1,14 @@
+// ..##########
+// ############
+// ####........
+// ####........
+// ####..######
+// ####....####
+// ############
+// ..##########
+
+#include "images.h"
+
+const image_t img_6x8_G = { 6, 8, false, 6, 0, {
+ 0x7F, 0xFC, 0x30, 0xDF, 0x3F, 0xDF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c
new file mode 100644
index 000000000..7b162baf3
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c
@@ -0,0 +1,14 @@
+// ####....####
+// ####....####
+// ..####..##..
+// ....######..
+// ..######....
+// ..##..####..
+// ####....####
+// ####....####
+
+#include "images.h"
+
+const image_t img_6x8_X = { 6, 8, false, 6, 0, {
+ 0xCF, 0x36, 0x8E, 0x71, 0x6C, 0xF3
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c
new file mode 100644
index 000000000..b39392948
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c
@@ -0,0 +1,14 @@
+// ####....####
+// ####....####
+// ####....####
+// ####....####
+// ..########..
+// ....####....
+// ....####....
+// ....####....
+
+#include "images.h"
+
+const image_t img_6x8_Y = { 6, 8, false, 6, 0, {
+ 0xCF, 0x3C, 0xF3, 0x78, 0xC3, 0x0C
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c
new file mode 100644
index 000000000..9904d08b4
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c
@@ -0,0 +1,14 @@
+// ############
+// ############
+// ........####
+// ......####..
+// ....####....
+// ..####......
+// ############
+// ############
+
+#include "images.h"
+
+const image_t img_6x8_Z = { 6, 8, false, 6, 0, {
+ 0xFF, 0xF0, 0xC6, 0x31, 0x8F, 0xFF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c
new file mode 100644
index 000000000..2a00713fd
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c
@@ -0,0 +1,14 @@
+// ........####
+// ........####
+// ........####
+// ..##########
+// ############
+// ####....####
+// ############
+// ..##########
+
+#include "images.h"
+
+const image_t img_6x8_d = { 6, 8, false, 6, 0, {
+ 0x0C, 0x30, 0xDF, 0xFF, 0x3F, 0xDF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c
new file mode 100644
index 000000000..086bdd2de
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c
@@ -0,0 +1,14 @@
+// ............
+// ............
+// ..########..
+// ############
+// ####....####
+// ####....####
+// ####....####
+// ####....####
+
+#include "images.h"
+
+const image_t img_6x8_n = { 6, 8, false, 6, 0, {
+ 0x00, 0x07, 0xBF, 0xCF, 0x3C, 0xF3
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c
new file mode 100644
index 000000000..c897aadff
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c
@@ -0,0 +1,14 @@
+// ............
+// ............
+// ##........##
+// ####....####
+// ####....####
+// ############
+// ..########..
+// ....####....
+
+#include "images.h"
+
+const image_t img_6x8_v = { 6, 8, false, 6, 0, {
+ 0x00, 0x08, 0x73, 0xCF, 0xF7, 0x8C
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_RIP.c b/applications/plugins/wii_ec_anal/gfx/img_RIP.c
new file mode 100644
index 000000000..55cb7bfc2
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_RIP.c
@@ -0,0 +1,122 @@
+// ################################################################################################################################################################################################################################################################
+// ################################################################################################################################################################################################################################################################
+// ####........................................................................................................................................................................................................................................................####
+// ####..##..##........................................................................................................................................................................................................................................##..##..####
+// ####....##....................##############..........########........##################........................##############......##..........##......##############......##############......##############......##############....................##....####
+// ####..##..##..................##############............######..........################........................##############....######......######....##############......##############......##############......##############..................##..##..####
+// ####....................................######..........##..##......................######....................####........######..######......######..####........######..######......######..............######..######......######........................####
+// ####........................######......######..........##..##..........######......##..##....................######......##..##..##..##......##..##..######......######..######......######..######......######..######......##..##........................####
+// ####........................######......##..##..........##..##..........######......##..##....................##..##......######..##..##......##..##..######......##..##..##..##......##..##..######......##..##..##..##......##..##........................####
+// ####........................##..##......##..##..........##..##..........##..##......##..##....................##..##..............##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##........................####
+// ####........................##..##......##..##..........##..##..........##..##......##..##....................##..##..............##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##..##..##......######........................####
+// ####........................##..##......######..........##..##..........##..##......######....................##..##..............##..##......##..##..##..##......######..##..##..##..##..##..##..##......######..##..##....................................####
+// ####........................##..##########..............##..##..........##..##########........................##..##..............######......######..##..##########......##..##......##..##..##..##########......##..##....................................####
+// ####........................##..##########..............##..##..........##..##########........................##..##................##############....##..##########......##..##......##..##..##..##########......##..##..########..........................####
+// ####........................##..##....######............##..##..........##..##................................##..##..................####..####......##..##......######..##..##..##..##..##..##..##....######....##..##..##########........................####
+// ####........................##..##....##..##............##..##..........##..##................................##..##....................##..##........##..##......##..##..##..##......##..##..##..##....##..##....##..##......##..##........................####
+// ####........................##..##....##..##............##..##..........##..##................................##..##....................##..##........##..##......##..##..##..##......##..##..##..##....##..##....##..##......##..##........................####
+// ####........................##..##....##..##............##..##..........##..##................................######......######........##..##........##..##......##..##..##..##......##..##..##..##....##..##....######......##..##........................####
+// ####........................##..##....##..##............##..##..........##..##................................######......##..##........##..##........######......######..######......######..##..##....##..##....######......##..##........................####
+// ####........................##..##....##..##............##..##..........##..##................................####........######........######........######......######..######......######..##..##....##..##....####........######........................####
+// ####........................######....######....####....######..####....######..####............................##############..........######..........##############......##############....######....######......##############..........................####
+// ####........................######......######..####..########..####..########..####............................##############........##########........##############......##############....######......######....##############..........................####
+// ####........................................................................................................................................................................................................................................................####
+// ####........................................................................................................................................................................................................................................................####
+// ####..........................................................................................................................................................................................####......####..............##############....................####
+// ####......................................................................................................................................##................................................##....##..##....##........####..............####................####
+// ####..........######..##..##..######......######..##..##..######..######..######..####..####..######......##......##..######..##....##..##..######......######..######......................####..##..####..##......##......................##..............####
+// ####............##....##..##..##..........##......##..##..##........##....##......##..##..##..##..........##......##..##..##..####..##........##........##..##..##............................####......####......##......##########..........##............####
+// ####............##....######..####........######....##....######....##....####....##..##..##..######......##..##..##..##..##..##..####........##........####....####..............................##..##........##......##..........##........##............####
+// ####............##....##..##..##..............##....##........##....##....##......##......##......##......##..##..##..##..##..##....##........##........##..##..##..................................##..........##....##..............##......##............####
+// ####............##....##..##..######......######....##....######....##....######..##......##..######......####..####..######..##....##........##........######..######............................######........##....##......####....##......##............####
+// ####............................................................................................................................................................................................##......##......##....##....##....##..##......##............####
+// ####..........................................................................................................................................................................................##........##......##....##....##..##....##......##............####
+// ####..........................................................................................................................................................................................##........##......##....##....##........##......##............####
+// ####..........######..##..##..######......######..######..####..####..######......##......##..######..######..##..##..######..##..##..######......##..##..######..##..##........................##........######......##......########......##..............####
+// ####............##....##..##..##..........##......##..##..##..##..##..##..........##......##....##......##....##..##..##..##..##..##....##........##..##..##..##..##..##........................##..............##......##..................####............####
+// ####............##....######..####........######..######..##..##..##..####........##..##..##....##......##....######..##..##..##..##....##..........##....##..##..##..##..........................##..............##########..............##....##..........####
+// ####............##....##..##..##..............##..##..##..##......##..##..........##..##..##....##......##....##..##..##..##..##..##....##..........##....##..##..##..##..........................##......................################........##........####
+// ####............##....##..##..######......######..##..##..##......##..######......####..####..######....##....##..##..######..######....##..........##....######..######............................####..........................................##........####
+// ####....................................................................................................................................................................................................##########################################..........####
+// ####........................................................................................................................................................................................................................................................####
+// ####........................................................................................................................................................................................................................................................####
+// ####......................................##########......##############......##############......##############............................##############......##############......##############......##############......................................####
+// ####......................................##########......##############......##############......##############............................##############......##############......##############......##############......................................####
+// ####......................................##..##..##....######......######..######......######..######......######........................######......######..######......######..######......######..######......######....................................####
+// ####..........................................##..##....######......######..##..##......######..######......######........................##..##......##..##..######......######..##..##......##..##..##..##......##..##....................................####
+// ####..........................................##..##....##..##......##..##..######......##..##..##..##......##..##........................######......##..##..##..##......##..##..######......##..##..######......##..##....................................####
+// ####..........................................##..##....##..##......##..##..............##..##..##..##......##..##....................................##..##..##..##......##..##..............##..##..............##..##....................................####
+// ####..........................................##..##....##..##......##..##..............##..##..##..##....####..##....................................##..##..##..##....####..##..............##..##..............##..##....................................####
+// ####..........................................##..##....######......##..##..............######..##..##....####..##....................................######..##..##....####..##..............######..............######....................................####
+// ####..........................................######........##########..##..............######..##..##..##..##..##....################........############....##..##..##..##..##......############........############......................................####
+// ####..........................................######........##########..##..........######......##..##..##..##..##....##............##......############......##..##..##..##..##....############........############........................................####
+// ####..........................................##..##................##..##..........######......##..####....##..##....################....######..............##..####....##..##..######..............######................................................####
+// ####..........................................##..##................##..##..........##..##......##..####....##..##........................##..##..............##..####....##..##..##..##..............##..##................................................####
+// ####..........................................##..##................##..##..........##..##......##..##......##..##........................##..##..............##..##......##..##..##..##..............##..##................................................####
+// ####..........................................##..##................##..##..........##..##......##..##......##..##........................##..##..............##..##......##..##..##..##..............##..##................................................####
+// ####..........................................##..##................######..........##..##......######......######........................##..##..............######......######..##..##..............##..##................................................####
+// ####..........................................##..##................######..........##..##......######......######........................##..##........####..######......######..##..##........####..##..##........####....................................####
+// ####..##..##..............................##############....############............######........##############..........................##################....##############....##################..##################............................##..##..####
+// ####....##..............................##################..############............######........##############..........................##################....##############....##################..##################..............................##....####
+// ####..##..##........................................................................................................................................................................................................................................##..##..####
+// ####........................................................................................................................................................................................................................................................####
+// ################################################################################################################################################################################################################################################################
+// ################################################################################################################################################################################################################################################################
+
+#include "images.h"
+
+const image_t img_RIP = { 128, 64, true, 837, 0x06, { // orig:1024, comp:18.26%
+ 0x06, 0x20, 0xFF, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xD4, 0x06, 0x0E, 0x00, 0x2B, 0xC8, 0x01, 0xFC,
+ 0x1E, 0x1F, 0xF0, 0x00, 0xFE, 0x20, 0x8F, 0xE3, 0xF8, 0xFE, 0x3F, 0x80, 0x13, 0xD4, 0x01, 0xFC,
+ 0x0E, 0x0F, 0xF0, 0x00, 0xFE, 0x71, 0xCF, 0xE3, 0xF8, 0xFE, 0x3F, 0x80, 0x2B, 0xC0, 0x00, 0x0E,
+ 0x0A, 0x00, 0x38, 0x01, 0x87, 0x71, 0xD8, 0x77, 0x1C, 0x07, 0x71, 0xC0, 0x03, 0xC0, 0x03, 0x8E,
+ 0x0A, 0x0E, 0x28, 0x01, 0xC5, 0x51, 0x5C, 0x77, 0x1D, 0xC7, 0x71, 0x40, 0x03, 0xC0, 0x03, 0x8A,
+ 0x0A, 0x0E, 0x28, 0x01, 0x47, 0x51, 0x5C, 0x55, 0x15, 0xC5, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x8A,
+ 0x0A, 0x0A, 0x28, 0x01, 0x40, 0x51, 0x54, 0x55, 0x15, 0x45, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x8A,
+ 0x0A, 0x0A, 0x28, 0x01, 0x40, 0x51, 0x54, 0x55, 0x15, 0x45, 0x51, 0xC0, 0x03, 0xC0, 0x02, 0x8E,
+ 0x0A, 0x0A, 0x38, 0x01, 0x40, 0x51, 0x54, 0x75, 0x55, 0x47, 0x50, 0x00, 0x03, 0xC0, 0x02, 0xF8,
+ 0x0A, 0x0B, 0xE0, 0x01, 0x40, 0x71, 0xD7, 0xC5, 0x15, 0x7C, 0x50, 0x00, 0x03, 0xC0, 0x02, 0xF8,
+ 0x0A, 0x0B, 0xE0, 0x01, 0x40, 0x3F, 0x97, 0xC5, 0x15, 0x7C, 0x57, 0x80, 0x03, 0xC0, 0x02, 0x9C,
+ 0x0A, 0x0A, 0x00, 0x01, 0x40, 0x1B, 0x14, 0x75, 0x55, 0x4E, 0x57, 0xC0, 0x03, 0xC0, 0x02, 0x94,
+ 0x0A, 0x0A, 0x00, 0x01, 0x40, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x94,
+ 0x0A, 0x0A, 0x00, 0x01, 0x40, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x94,
+ 0x0A, 0x0A, 0x00, 0x01, 0xC7, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x71, 0x40, 0x03, 0xC0, 0x02, 0x94,
+ 0x0A, 0x0A, 0x00, 0x01, 0xC5, 0x0A, 0x1C, 0x77, 0x1D, 0x4A, 0x71, 0x40, 0x03, 0xC0, 0x02, 0x94,
+ 0x0A, 0x0A, 0x00, 0x01, 0x87, 0x0E, 0x1C, 0x77, 0x1D, 0x4A, 0x61, 0xC0, 0x03, 0xC0, 0x03, 0x9C,
+ 0xCE, 0xCE, 0xC0, 0x00, 0xFE, 0x0E, 0x0F, 0xE3, 0xF9, 0xCE, 0x3F, 0x80, 0x03, 0xC0, 0x03, 0x8E,
+ 0xDE, 0xDE, 0xC0, 0x00, 0xFE, 0x1F, 0x0F, 0xE3, 0xF9, 0xC7, 0x3F, 0x80, 0x03, 0xC0, 0x06, 0x0E,
+ 0x00, 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0A, 0x00, 0x01, 0x8C, 0x07, 0xF0, 0x03,
+ 0xC0, 0x06, 0x07, 0x00, 0x04, 0x00, 0x00, 0x02, 0x52, 0x18, 0x0C, 0x03, 0xC1, 0xD5, 0xC7, 0x57,
+ 0x77, 0x6D, 0xC4, 0x5D, 0x2B, 0x8E, 0xE0, 0x03, 0x5A, 0x20, 0x02, 0x03, 0xC0, 0x95, 0x04, 0x54,
+ 0x24, 0x55, 0x04, 0x55, 0xA1, 0x0A, 0x80, 0x01, 0x8C, 0x47, 0xC1, 0x03, 0xC0, 0x9D, 0x87, 0x27,
+ 0x26, 0x55, 0xC5, 0x55, 0x61, 0x0C, 0xC0, 0x00, 0x50, 0x88, 0x21, 0x03, 0xC0, 0x95, 0x01, 0x21,
+ 0x24, 0x44, 0x45, 0x55, 0x21, 0x0A, 0x80, 0x00, 0x20, 0x90, 0x11, 0x03, 0xC0, 0x95, 0xC7, 0x27,
+ 0x27, 0x45, 0xC6, 0xDD, 0x21, 0x0E, 0xE0, 0x00, 0x70, 0x91, 0x91, 0x03, 0xC0, 0x06, 0x0B, 0x00,
+ 0x88, 0x92, 0x51, 0x03, 0xC0, 0x06, 0x0A, 0x00, 0x01, 0x08, 0x92, 0x91, 0x03, 0xC0, 0x06, 0x0A,
+ 0x00, 0x01, 0x08, 0x92, 0x11, 0x03, 0xC1, 0xD5, 0xC7, 0x76, 0xDC, 0x45, 0xDD, 0x5D, 0x5C, 0x57,
+ 0x50, 0x00, 0x87, 0x11, 0xE2, 0x03, 0xC0, 0x95, 0x04, 0x55, 0x50, 0x44, 0x89, 0x55, 0x48, 0x55,
+ 0x50, 0x00, 0x80, 0x88, 0x03, 0x03, 0xC0, 0x9D, 0x87, 0x75, 0x58, 0x54, 0x89, 0xD5, 0x48, 0x25,
+ 0x50, 0x00, 0x40, 0x7C, 0x04, 0x83, 0xC0, 0x95, 0x01, 0x54, 0x50, 0x54, 0x89, 0x55, 0x48, 0x25,
+ 0x50, 0x00, 0x40, 0x07, 0xF8, 0x43, 0xC0, 0x95, 0xC7, 0x54, 0x5C, 0x6D, 0xC9, 0x5D, 0xC8, 0x27,
+ 0x70, 0x00, 0x30, 0x00, 0x00, 0x43, 0xC0, 0x06, 0x0B, 0x00, 0x0F, 0xFF, 0xFF, 0x83, 0xC0, 0x06,
+ 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x00, 0x07, 0xC7, 0xF1, 0xFC, 0x7F, 0x00,
+ 0x03, 0xF8, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0xC7, 0xF1, 0xFC, 0x7F, 0x00,
+ 0x03, 0xF8, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x05, 0x4E, 0x3B, 0x8E, 0xE3, 0x80,
+ 0x07, 0x1D, 0xC7, 0x71, 0xDC, 0x70, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4E, 0x3A, 0x8E, 0xE3, 0x80,
+ 0x05, 0x15, 0xC7, 0x51, 0x54, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x2B, 0x8A, 0xA2, 0x80,
+ 0x07, 0x15, 0x45, 0x71, 0x5C, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x28, 0x0A, 0xA2, 0x80,
+ 0x00, 0x15, 0x45, 0x01, 0x40, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x28, 0x0A, 0xA6, 0x80,
+ 0x00, 0x15, 0x4D, 0x01, 0x40, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4E, 0x28, 0x0E, 0xA6, 0x80,
+ 0x00, 0x1D, 0x4D, 0x01, 0xC0, 0x70, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xC3, 0xE8, 0x0E, 0xAA, 0x9F,
+ 0xE1, 0xF9, 0x55, 0x1F, 0x87, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xC3, 0xE8, 0x38, 0xAA, 0x90,
+ 0x23, 0xF1, 0x55, 0x3F, 0x0F, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x38, 0xB2, 0x9F,
+ 0xE7, 0x01, 0x65, 0x70, 0x1C, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xB2, 0x80,
+ 0x05, 0x01, 0x65, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xA2, 0x80,
+ 0x05, 0x01, 0x45, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xA2, 0x80,
+ 0x05, 0x01, 0x45, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x38, 0x28, 0xE3, 0x80,
+ 0x05, 0x01, 0xC7, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x38, 0x28, 0xE3, 0x80,
+ 0x05, 0x0D, 0xC7, 0x50, 0xD4, 0x30, 0x00, 0x03, 0xD4, 0x00, 0x07, 0xF3, 0xF0, 0x38, 0x7F, 0x00,
+ 0x07, 0xFC, 0xFE, 0x7F, 0xDF, 0xF0, 0x00, 0x2B, 0xC8, 0x00, 0x0F, 0xFB, 0xF0, 0x38, 0x7F, 0x00,
+ 0x07, 0xFC, 0xFE, 0x7F, 0xDF, 0xF0, 0x00, 0x13, 0xD4, 0x06, 0x0E, 0x00, 0x2B, 0xC0, 0x06, 0x0E,
+ 0x00, 0x03, 0x06, 0x20, 0xFF
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c b/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c
new file mode 100644
index 000000000..2fc6b5f23
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c
@@ -0,0 +1,17 @@
+// ####..##
+// ##..####
+// ####..##
+// ##..####
+// ####..##
+// ##..####
+// ####..##
+// ##..####
+// ####..##
+// ##..####
+// ####..##
+
+#include "images.h"
+
+const image_t img_cc_Cable = { 4, 11, true, 4, 0x00, { // orig:6, comp:33.33%
+ 0x00, 0x05, 0xDB, 0xD0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c b/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c
new file mode 100644
index 000000000..dd189cb7e
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c
@@ -0,0 +1,25 @@
+// ................##................
+// ............##########............
+// ....############..############....
+// ....######..............######....
+// ....####..................####....
+// ....##......................##....
+// ..####......................####..
+// ..####......................####..
+// ####..........................####
+// ..####......................####..
+// ..####......................####..
+// ....##......................##....
+// ....####..................####....
+// ....######..............######....
+// ....############..############....
+// ............##########............
+// ................##................
+
+#include "images.h"
+
+const image_t img_cc_Joy = { 17, 17, false, 37, 0, {
+ 0x00, 0x80, 0x01, 0xF0, 0x0F, 0xDF, 0x87, 0x01, 0xC3, 0x00, 0x61, 0x00, 0x11, 0x80, 0x0C, 0xC0,
+ 0x06, 0xC0, 0x01, 0xB0, 0x01, 0x98, 0x00, 0xC4, 0x00, 0x43, 0x00, 0x61, 0xC0, 0x70, 0xFD, 0xF8,
+ 0x07, 0xC0, 0x00, 0x80, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c b/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c
new file mode 100644
index 000000000..8e7bd5ed9
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c
@@ -0,0 +1,92 @@
+// ..................................................##################................................................................................................##################..................................................
+// ......................................############................##............##########....................................................##########............##................############......................................
+// ..................................####............................##..........##..........##......................####......................##..........##..........##............................####..................................
+// ................................##................................##############..........####################################################..........##############................................##................................
+// ............................######................................##..........##..........##......................####......................##..........##..........##................................######............................
+// ..........................##..##....................################..........##..........##......................####......................##..........##..........################....................##..##..........................
+// ........................##....##........############............................##########....................................................##########............................############........##....##........................
+// ......................##......##########........................................................................................................................................................##########......##......................
+// ....................##..........########################################################################################################################################################################..........##....................
+// ..................##......########....................................................................................................................................................................########......##..................
+// ................##....######................................................................................................................................................................................######....##................
+// ..............##....####........................................................................................................................................................................................####....##..............
+// ............##########............................................................................................................................................................................................##########............
+// ............######......................................................................................####......####..####..####....................................................................................######............
+// ..........######......................##################................................................####..##..####..................................................................######..........................######..........
+// ..........####........................##################................................................####..##..####..####..####....................................................##########..........................####..........
+// ........####..........................####..........####................................................####..##..####..####..####..................................................####......####..........................####........
+// ......######..........................####..........####..................................................####..####....####..####................................................####..........####........................######......
+// ......####............................####....##....####........................................................................................................................####....##..##....####........................####......
+// ......##..............................####....##....####........................................................................................................................####......##......####..........................####....
+// ....####..............................####....##....####........................................................................................................................####....##..##....####..........................####....
+// ....##..................##################..........##################............................................................................................######..........####..........####..........######..............##....
+// ..####..................##################..........##################..........................................................................................##########..........####......####..........##########............####..
+// ..##....................####......................................####..........................########........########........########......................####......####..........##########..........####......####............##..
+// ..##....................####......................................####........................####....####....####....####....####....####..................####..........####..........######..........####....##....####..........##..
+// ####....................####....######..................######....####........................##........##....##........##....##........##................####....##..##....####......................####....##..##....####........####
+// ##......................####......................................####........................##........##....##........##....##........##................####....######....####......................####....######....####..........##
+// ##......................####......................................####........................####....####....####....####....####....####................####........##....####......................####....##..##....####..........##
+// ##......................##################..........##################..........................########........########........########....................####....##....####..........######..........####..........####............##
+// ##......................##################..........##################........................................................................................####......####..........##########..........####......####..............##
+// ##....................................####....##....####........................................................................................................##########..........####......####..........##########................##
+// ##....................................####....##....####..........................................................................................................######..........####..##......####..........######..................##
+// ##....................................####....##....####........................................................................................................................####....##........####................................##
+// ##....................................####..........####........................................................................................................................####....####......####................................##
+// ##....................................####..........####........................................................................................................................####....##..##....####................................##
+// ####..................................##################..........................................................................................................................####....####..####................................####
+// ..##..................................##################............................................................................................................................####......####..................................##..
+// ..##..................................................................................................................................................................................##########....................................##..
+// ..####..................................................................................................................................................................................######....................................####..
+// ....##............................................................................................................................................................................................................................##....
+// ....##............................................................................................................................................................................................................................##....
+// ....####........................................................................................................................................................................................................................####....
+// ......##........................................................................................................................................................................................................................##......
+// ......####....................................................................................................................................................................................................................####......
+// ........####................................................................................................................................................................................................................####........
+// ..........####............................................................................................................................................................................................................####..........
+// ............####........................................................................................................................................................................................................####............
+// ..............####....................................................................................................................................................................................................####..............
+// ................######............................................................................................................................................................................................######................
+// ....................####........................................................................................................................................................................................####....................
+// ......................######................................................................................................................................................................................######......................
+// ..........................########....................................................................................................................................................................########..........................
+// ................................########################################################################################################################################################################................................
+
+#include "images.h"
+
+const image_t img_cc_Main = { 116, 53, true, 542, 0x05, { // orig:769, comp:29.52%
+ 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x05, 0x05, 0x00, 0x3F, 0xE0, 0x05, 0x04, 0x00, 0x01, 0xF8, 0x04,
+ 0x0F, 0x80, 0x00, 0x00, 0x1F, 0x02, 0x01, 0xF8, 0x05, 0x04, 0x00, 0x60, 0x00, 0x41, 0x04, 0x00,
+ 0x60, 0x02, 0x08, 0x20, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0xF0, 0x7F, 0xFF, 0xFF,
+ 0xE0, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x41, 0x04, 0x00, 0x60, 0x02, 0x08,
+ 0x20, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x50, 0x03, 0xFC, 0x10, 0x40, 0x06, 0x00, 0x20, 0x83, 0xFC,
+ 0x00, 0xA0, 0x00, 0x00, 0x09, 0x0F, 0xC0, 0x00, 0xF8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x3F, 0x09,
+ 0x00, 0x00, 0x01, 0x1F, 0x05, 0x09, 0x00, 0x0F, 0x88, 0x00, 0x00, 0x20, 0x05, 0x0A, 0xFF, 0xF0,
+ 0x40, 0x00, 0x04, 0x78, 0x05, 0x09, 0x00, 0x01, 0xE2, 0x00, 0x00, 0x9C, 0x05, 0x0A, 0x00, 0x03,
+ 0x90, 0x00, 0x13, 0x05, 0x0B, 0x00, 0x0C, 0x80, 0x03, 0xE0, 0x05, 0x0B, 0x00, 0x7C, 0x00, 0x38,
+ 0x05, 0x05, 0x00, 0xC6, 0xD8, 0x05, 0x04, 0x00, 0x01, 0xC0, 0x07, 0x00, 0x1F, 0xF0, 0x00, 0x00,
+ 0x0D, 0x60, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x60, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xD6,
+ 0xD8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x60, 0x0C, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0D, 0x6D, 0x80,
+ 0x00, 0x00, 0x31, 0x80, 0x03, 0x01, 0xC0, 0x01, 0x83, 0x00, 0x00, 0x00, 0x6C, 0xD8, 0x00, 0x00,
+ 0x06, 0x0C, 0x00, 0x38, 0x18, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, 0xCA, 0x60, 0x01, 0x81, 0x00,
+ 0x01, 0x93, 0x05, 0x07, 0x00, 0x0C, 0x46, 0x00, 0x0C, 0x30, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00,
+ 0xCA, 0x60, 0x00, 0xC2, 0x00, 0xFF, 0x83, 0xFE, 0x05, 0x05, 0x00, 0x07, 0x06, 0x0C, 0x1C, 0x04,
+ 0x60, 0x0F, 0xF8, 0x3F, 0xE0, 0x05, 0x05, 0x00, 0xF8, 0x31, 0x83, 0xE0, 0x64, 0x00, 0xC0, 0x00,
+ 0x06, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x18, 0xC1, 0xF0, 0x63, 0x02, 0x40, 0x0C, 0x00, 0x00, 0x60,
+ 0x01, 0x99, 0x99, 0x98, 0x03, 0x06, 0x0E, 0x0C, 0x98, 0x2C, 0x00, 0xCE, 0x00, 0xE6, 0x00, 0x10,
+ 0x90, 0x90, 0x80, 0x65, 0x30, 0x01, 0x94, 0xC3, 0x80, 0x0C, 0x00, 0x00, 0x60, 0x01, 0x09, 0x09,
+ 0x08, 0x06, 0x73, 0x00, 0x19, 0xCC, 0x18, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x19, 0x99, 0x99, 0x80,
+ 0x61, 0x30, 0x01, 0x94, 0xC1, 0x80, 0x0F, 0xF8, 0x3F, 0xE0, 0x00, 0xF0, 0xF0, 0xF0, 0x03, 0x26,
+ 0x0E, 0x0C, 0x18, 0x18, 0x00, 0xFF, 0x83, 0xFE, 0x05, 0x05, 0x00, 0x18, 0xC1, 0xF0, 0x63, 0x01,
+ 0x80, 0x00, 0x19, 0x30, 0x05, 0x06, 0x00, 0xF8, 0x31, 0x83, 0xE0, 0x18, 0x00, 0x01, 0x93, 0x05,
+ 0x06, 0x00, 0x07, 0x06, 0x8C, 0x1C, 0x01, 0x80, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, 0xC8, 0x60,
+ 0x00, 0x18, 0x00, 0x01, 0x83, 0x05, 0x07, 0x00, 0x0C, 0xC6, 0x00, 0x01, 0x80, 0x00, 0x18, 0x30,
+ 0x05, 0x07, 0x00, 0xCA, 0x60, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x05, 0x07, 0x00, 0x06, 0x6C, 0x00,
+ 0x03, 0x40, 0x00, 0x1F, 0xF0, 0x05, 0x07, 0x00, 0x31, 0x80, 0x00, 0x24, 0x05, 0x0A, 0x00, 0x01,
+ 0xF0, 0x00, 0x02, 0x60, 0x05, 0x0A, 0x00, 0x0E, 0x00, 0x00, 0x62, 0x05, 0x0D, 0x00, 0x04, 0x20,
+ 0x05, 0x0D, 0x00, 0x43, 0x05, 0x0D, 0x00, 0x0C, 0x10, 0x05, 0x0D, 0x00, 0x81, 0x80, 0x05, 0x0C,
+ 0x00, 0x18, 0x0C, 0x05, 0x0C, 0x00, 0x03, 0x00, 0x60, 0x05, 0x0C, 0x00, 0x60, 0x03, 0x05, 0x0C,
+ 0x00, 0x0C, 0x00, 0x18, 0x05, 0x0B, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x05, 0x0B, 0x00, 0x70, 0x00,
+ 0x03, 0x05, 0x0B, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x05, 0x0A, 0x00, 0x03, 0x80, 0x00, 0x00, 0x78,
+ 0x05, 0x09, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x05, 0x0A, 0xFF, 0xF0, 0x00, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c
new file mode 100644
index 000000000..4d54cbf22
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c
@@ -0,0 +1,13 @@
+// ##############
+// ######..######
+// ####..##..####
+// ####......####
+// ####..##..####
+// ##############
+// ##############
+
+#include "images.h"
+
+const image_t img_cc_btn_A1 = { 7, 7, false, 7, 0, {
+ 0xFF, 0xDF, 0x5E, 0x3D, 0x7F, 0xFF, 0x80
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c
new file mode 100644
index 000000000..89d357282
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c
@@ -0,0 +1,13 @@
+// ##############
+// ####..########
+// ####..########
+// ####....######
+// ####..##..####
+// ######....####
+// ##############
+
+#include "images.h"
+
+const image_t img_cc_btn_B1 = { 7, 7, false, 7, 0, {
+ 0xFF, 0xBF, 0x7E, 0x7D, 0x7C, 0xFF, 0x80
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c
new file mode 100644
index 000000000..a1e7f2876
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c
@@ -0,0 +1,13 @@
+// ##############
+// ##############
+// ####..##..####
+// ######..######
+// ####..##..####
+// ##############
+// ##############
+
+#include "images.h"
+
+const image_t img_cc_btn_X1 = { 7, 7, false, 7, 0, {
+ 0xFF, 0xFF, 0x5F, 0x7D, 0x7F, 0xFF, 0x80
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c
new file mode 100644
index 000000000..01f66b4c7
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c
@@ -0,0 +1,13 @@
+// ##############
+// ##############
+// ####..##..####
+// ####......####
+// ########..####
+// ######..######
+// ##############
+
+#include "images.h"
+
+const image_t img_cc_btn_Y1 = { 7, 7, false, 7, 0, {
+ 0xFF, 0xFF, 0x5E, 0x3F, 0x7D, 0xFF, 0x80
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c
new file mode 100644
index 000000000..698fcfdd6
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c
@@ -0,0 +1,11 @@
+// ##############
+// ##############
+// ####......####
+// ##############
+// ##############
+
+#include "images.h"
+
+const image_t img_cc_pad_LR1 = { 7, 5, false, 5, 0, {
+ 0xFF, 0xFF, 0x1F, 0xFF, 0xE0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c
new file mode 100644
index 000000000..b7d104ee4
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c
@@ -0,0 +1,13 @@
+// ##########
+// ##########
+// ####..####
+// ####..####
+// ####..####
+// ##########
+// ##########
+
+#include "images.h"
+
+const image_t img_cc_pad_UD1 = { 5, 7, false, 5, 0, {
+ 0xFF, 0xF7, 0xBD, 0xFF, 0xE0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c
new file mode 100644
index 000000000..cf4d7159b
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c
@@ -0,0 +1,12 @@
+// ......##############....##....##..
+// ..####..##....##....##....##....##
+// ##....##....##....##....##....##..
+// ##..##....##....##....##....##....
+// ..##....##....##....##############
+// ##....##############..............
+
+#include "images.h"
+
+const image_t img_cc_trg_L1 = { 17, 6, false, 13, 0, {
+ 0x1F, 0xC9, 0x34, 0x92, 0x64, 0x92, 0x54, 0x92, 0x44, 0x93, 0xFC, 0xFE, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c
new file mode 100644
index 000000000..9e61a64cc
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c
@@ -0,0 +1,12 @@
+// ......##############..##..##..##..
+// ..####..##..##..##..##..##..##..##
+// ####..##..##..##..##..##..##..##..
+// ##..##..##..##..##..##..##..##..##
+// ..##..##..##..##..################
+// ##..##..############..............
+
+#include "images.h"
+
+const image_t img_cc_trg_L2 = { 17, 6, true, 12, 0x01, { // orig:13, comp:7.69%
+ 0x1F, 0xD5, 0x35, 0x55, 0x75, 0x01, 0x04, 0x55, 0x57, 0xFD, 0x7E, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c
new file mode 100644
index 000000000..1b06de5ee
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c
@@ -0,0 +1,12 @@
+// ......############..####..####..##
+// ..######..####..####..####..####..
+// ######..####..####..####..####..##
+// ####..####..####..####..####..####
+// ##..####..####..##################
+// ..####..############..............
+
+#include "images.h"
+
+const image_t img_cc_trg_L3 = { 17, 6, false, 13, 0, {
+ 0x1F, 0xB6, 0xBB, 0x6D, 0xBB, 0x6D, 0xBB, 0x6D, 0xBB, 0x6F, 0xFB, 0x7E, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c
new file mode 100644
index 000000000..12f877ab1
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c
@@ -0,0 +1,12 @@
+// ......############################
+// ..################################
+// ##################################
+// ##################################
+// ##################################
+// ####################..............
+
+#include "images.h"
+
+const image_t img_cc_trg_L4 = { 17, 6, true, 8, 0x01, { // orig:13, comp:38.46%
+ 0x1F, 0xFF, 0xBF, 0x01, 0x08, 0xFF, 0xFE, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c
new file mode 100644
index 000000000..a196c0fe1
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c
@@ -0,0 +1,12 @@
+// ..##....##....##############......
+// ##....##....##....##....##..####..
+// ..##....##....##....##....##....##
+// ....##....##....##....##....##..##
+// ##############....##....##....##..
+// ..............##############....##
+
+#include "images.h"
+
+const image_t img_cc_trg_R1 = { 17, 6, false, 13, 0, {
+ 0x49, 0xFC, 0x49, 0x25, 0x92, 0x49, 0x24, 0x92, 0x5F, 0xE4, 0x90, 0x0F, 0xE4
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c
new file mode 100644
index 000000000..ea5458f39
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c
@@ -0,0 +1,12 @@
+// ..##..##..##..##############......
+// ##..##..##..##..##..##..##..####..
+// ..##..##..##..##..##..##..##..####
+// ##..##..##..##..##..##..##..##..##
+// ################..##..##..##..##..
+// ..............############..##..##
+
+#include "images.h"
+
+const image_t img_cc_trg_R2 = { 17, 6, false, 13, 0, {
+ 0x55, 0xFC, 0x55, 0x55, 0x95, 0x55, 0x75, 0x55, 0x5F, 0xF5, 0x50, 0x0F, 0xD4
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c
new file mode 100644
index 000000000..94cf02bf0
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c
@@ -0,0 +1,12 @@
+// ##..####..####..############......
+// ..####..####..####..####..######..
+// ##..####..####..####..####..######
+// ####..####..####..####..####..####
+// ##################..####..####..##
+// ..............############..####..
+
+#include "images.h"
+
+const image_t img_cc_trg_R3 = { 17, 6, false, 13, 0, {
+ 0xB6, 0xFC, 0x36, 0xDB, 0xAD, 0xB6, 0xFB, 0x6D, 0xBF, 0xFB, 0x68, 0x0F, 0xD8
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c
new file mode 100644
index 000000000..c90fbe20b
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c
@@ -0,0 +1,12 @@
+// ############################......
+// ################################..
+// ##################################
+// ##################################
+// ##################################
+// ..............####################
+
+#include "images.h"
+
+const image_t img_cc_trg_R4 = { 17, 6, true, 11, 0x00, { // orig:13, comp:15.38%
+ 0xFF, 0xFC, 0x7F, 0xFF, 0xBF, 0x00, 0x05, 0xFF, 0xF8, 0x0F, 0xFC
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c b/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c
new file mode 100644
index 000000000..97f09ac17
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c
@@ -0,0 +1,81 @@
+// ....##########################################........##..........##........##############........##############........##############........##############............................................................................................
+// ....##########################################......######......######......##############........##############........##############........##############............................................................................................
+// ############..............................######....######......######....####........######....######......######................######....######......######..............######..######..######......................................................
+// ############..............................##..##....##..##......##..##....######......######....######......######....######......######....######......##..##..............##......##........##........................................................
+// ############..............................##..##....##..##......##..##....######......##..##....##..##......##..##....######......##..##....##..##......##..##..............####....######....##........................................................
+// ####....####..............................##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##..............##..........##....##........................................................
+// ############..............................######....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......######..............######..######....##..##....................................................
+// ############........................................##..##......##..##....##..##......######....##..##..##..##..##....##..##......######....##..##......................................................................................................
+// ####....####........................................######......######....##..##########........##..##......##..##....##..##########........##..##........................####..######..######..##......................................................
+// ######..####........####..............................##############......##..##########........##..##......##..##....##..##########........##..##..########................##..##..##..##..##..##..##..................................................
+// ####..######........####................................####..####........##..##......######....##..##..##..##..##....##..##....######......##..##..##########..............##..######..######..######..................................................
+// ######..####................####..........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......##..##......##..##..............##......##..##..##......##..................................................
+// ####..######..............##....##........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......##..##......##..##............######....##..######......##..................................##..............
+// ######..####..............##....##........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......######......##..##........................................................................##..##............
+// ####..######................####..........................##..##..........######......######....######......######....##..##....##..##......######......##..##..................................................................######......##..........
+// ####....####..............................................######..........######......######....######......######....##..##....##..##......####........######..................................................................####....##....##........
+// ####....####..............................................######............##############........##############......######....######........##############....................................................................##....##..##....##......
+// ####....####............................................##########..........##############........##############......######......######......##############..............................................................######....##......##....##....
+// ####....####..............................................................................................................................................................................................................####........##..##..##....##..
+// ####....####..............................................................................................................................................................................................................##....##......##......##....##
+// ####....####....................##############........##..........##........##############......##################......##############........##############........##############..................................######....##..........##..##....####
+// ####....####....................##############......######......######......##############......##################......##############........##############........##############..................................####....##......##......##....######
+// ####..######..................####........######....######......######....####........######....####..######..####....####....##########....##################....####........######................................##........##..##..##........########
+// ######..####..................######......##..##....##..##......##..##....######......##..##..........##..##..........######................######..##..######....######......##..##..........................######....##......##......##....########..
+// ####..######..................##..##......##..##....##..##......##..##....##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##..........................####....##..##..........##....##########..
+// ######..####..................##..##......##..##....##..##......##..##....##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##..........................##....##......##......##....##############
+// ####..######..................##..##......######....##..##......##..##....##..##......######..........##..##..........##..##................##..##..##..##..##....##..##......######....................######............##..##........########....####
+// ######..####..................######................##..##......##..##....######......................##..##..........##..##....######......##..##..##..##..##....######................................####....................##....##########........
+// ####....####....................############........######......######......############..............##..##..........##..########..........##..##..##..##..##......############........................##....##..............##....##############......
+// ############........................##########........##############............##########............##..##..........##..########..........##..##..##..##..##..........##########....................##....##..##..........##....########....####......
+// ############..............................######........####..####....................######..........##..##..........##..##....######......##..##..##..##..##................######................##..##........##............##########..............
+// ####....####..................######......##..##..........##..##..........######......##..##..........##..##..........##..##................##..##......##..##....######......##..##................####............##..##....##############............
+// ############..................##..##......##..##..........##..##..........##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##................####....##........##....########....####............
+// ############..........####....##..##......##..##..........##..##..........##..##......##..##..........##..##..........##..##................##..##......##..##....##..##......##..##..................##########....##....##########....................
+// ############..........####....##..##......######..........##..##..........##..##......######..........##..##..........######................##..##......##..##....##..##......######....................######..........##############..................
+// ############..........####....######........####..........######..........######........####..........##..##..........####....##########....##..##......##..##....######........####......................####........########....####..................
+// ....######################......##############............######............##############............######............##############......######......######......##############..........................######..##########..........................
+// ....######################......##############..........##########..........##############............######............##############......######......######......##############............................##################........................
+// ................................................................................................................................................................................................................########....####........................
+// ..................................................................................................................................................................................................................####..................................
+
+#include "images.h"
+
+const image_t img_csLogo_FULL = { 124, 40, true, 571, 0x0B, { // orig:620, comp:7.90%
+ 0x3F, 0xFF, 0xFE, 0x10, 0x43, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x0B, 0x05, 0x00, 0x03, 0xFF, 0xFF,
+ 0xE3, 0x8E, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC0, 0x0B, 0x05, 0x00, 0xFC, 0x00, 0x07, 0x38, 0xE6,
+ 0x1C, 0xE3, 0x80, 0x73, 0x8E, 0x03, 0xBB, 0x80, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x52, 0x8A, 0x71,
+ 0xCE, 0x39, 0xC7, 0x38, 0xA0, 0x22, 0x10, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x05, 0x28, 0xA7, 0x14,
+ 0xA2, 0x9C, 0x52, 0x8A, 0x03, 0x39, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x52, 0x8A, 0x51, 0x4A,
+ 0x29, 0x45, 0x28, 0xA0, 0x20, 0x90, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x07, 0x28, 0xA5, 0x14, 0xA2,
+ 0x94, 0x52, 0x8E, 0x03, 0xB9, 0x40, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x02, 0x8A, 0x51, 0xCA, 0xA9,
+ 0x47, 0x28, 0x0B, 0x06, 0x00, 0xCC, 0x00, 0x00, 0x38, 0xE5, 0xF0, 0xA2, 0x97, 0xC2, 0x80, 0x06,
+ 0xEE, 0x80, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x01, 0xFC, 0x5F, 0x0A, 0x29, 0x7C, 0x2B, 0xC0, 0x2A,
+ 0xAA, 0x00, 0x00, 0x00, 0xDC, 0x30, 0x00, 0x0D, 0x85, 0x1C, 0xAA, 0x94, 0xE2, 0xBE, 0x02, 0xEE,
+ 0xE0, 0x00, 0x00, 0x0E, 0xC0, 0x30, 0x00, 0x50, 0x51, 0x4A, 0x29, 0x4A, 0x28, 0xA0, 0x22, 0xA2,
+ 0x00, 0x00, 0x00, 0xDC, 0x04, 0x80, 0x05, 0x05, 0x14, 0xA2, 0x94, 0xA2, 0x8A, 0x07, 0x2E, 0x20,
+ 0x00, 0x08, 0x0E, 0xC0, 0x48, 0x00, 0x50, 0x51, 0x4A, 0x29, 0x4A, 0x38, 0xA0, 0x00, 0x00, 0x00,
+ 0x01, 0x40, 0xDC, 0x03, 0x00, 0x05, 0x07, 0x1C, 0xE3, 0x94, 0xA3, 0x8A, 0x0B, 0x04, 0x00, 0xE2,
+ 0x0C, 0xC0, 0x00, 0x00, 0x70, 0x71, 0xCE, 0x39, 0x4A, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x0C, 0x90,
+ 0xCC, 0x00, 0x00, 0x07, 0x03, 0xF8, 0x7F, 0x1C, 0xE1, 0xFC, 0x0B, 0x04, 0x00, 0x94, 0x8C, 0xC0,
+ 0x00, 0x00, 0xF8, 0x3F, 0x87, 0xF1, 0xC7, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x72, 0x24, 0xCC, 0x0B,
+ 0x0C, 0x00, 0x06, 0x15, 0x2C, 0xC0, 0x0B, 0x0C, 0x00, 0x48, 0x89, 0xCC, 0x00, 0xFE, 0x10, 0x43,
+ 0xF8, 0xFF, 0x8F, 0xE1, 0xFC, 0x3F, 0x80, 0x00, 0x39, 0x05, 0x3C, 0xC0, 0x0F, 0xE3, 0x8E, 0x3F,
+ 0x8F, 0xF8, 0xFE, 0x1F, 0xC3, 0xF8, 0x00, 0x03, 0x22, 0x27, 0xDC, 0x01, 0x87, 0x38, 0xE6, 0x1C,
+ 0xDD, 0x99, 0xF3, 0xFE, 0x61, 0xC0, 0x00, 0x21, 0x50, 0xFE, 0xC0, 0x1C, 0x52, 0x8A, 0x71, 0x41,
+ 0x41, 0xC0, 0x3A, 0xE7, 0x14, 0x00, 0x1C, 0x88, 0x9E, 0xDC, 0x01, 0x45, 0x28, 0xA5, 0x14, 0x14,
+ 0x14, 0x02, 0xAA, 0x51, 0x40, 0x01, 0x94, 0x13, 0xEE, 0xC0, 0x14, 0x52, 0x8A, 0x51, 0x41, 0x41,
+ 0x40, 0x2A, 0xA5, 0x14, 0x00, 0x12, 0x22, 0x7F, 0xDC, 0x01, 0x47, 0x28, 0xA5, 0x1C, 0x14, 0x14,
+ 0x02, 0xAA, 0x51, 0xC0, 0x0E, 0x05, 0x0F, 0x3E, 0xC0, 0x1C, 0x02, 0x8A, 0x70, 0x01, 0x41, 0x4E,
+ 0x2A, 0xA7, 0x00, 0x00, 0xC0, 0x09, 0xF0, 0xCC, 0x00, 0xFC, 0x38, 0xE3, 0xF0, 0x14, 0x17, 0x82,
+ 0xAA, 0x3F, 0x00, 0x09, 0x01, 0x3F, 0x8F, 0xC0, 0x03, 0xE1, 0xFC, 0x0F, 0x81, 0x41, 0x78, 0x2A,
+ 0xA0, 0xF8, 0x01, 0x28, 0x27, 0x98, 0xFC, 0x00, 0x07, 0x0D, 0x80, 0x1C, 0x14, 0x14, 0xE2, 0xAA,
+ 0x01, 0xC0, 0x28, 0x40, 0xF8, 0x0C, 0xC0, 0x1C, 0x50, 0x50, 0x71, 0x41, 0x41, 0x40, 0x28, 0xA7,
+ 0x14, 0x03, 0x02, 0x9F, 0xC0, 0xFC, 0x01, 0x45, 0x05, 0x05, 0x14, 0x14, 0x14, 0x02, 0xAA, 0x51,
+ 0x40, 0x32, 0x13, 0xCC, 0x0F, 0xC1, 0x94, 0x50, 0x50, 0x51, 0x41, 0x41, 0x40, 0x28, 0xA5, 0x14,
+ 0x01, 0xF2, 0x7C, 0x00, 0xFC, 0x19, 0x47, 0x05, 0x05, 0x1C, 0x14, 0x1C, 0x02, 0x8A, 0x51, 0xC0,
+ 0x0E, 0x0F, 0xE0, 0x0F, 0xC1, 0x9C, 0x30, 0x70, 0x70, 0xC1, 0x41, 0x9F, 0x28, 0xA7, 0x0C, 0x00,
+ 0x61, 0xE6, 0x00, 0x3F, 0xF8, 0xFE, 0x07, 0x03, 0xF8, 0x1C, 0x0F, 0xE3, 0x8E, 0x3F, 0x80, 0x03,
+ 0xBE, 0x00, 0x03, 0xFF, 0x8F, 0xE0, 0xF8, 0x3F, 0x81, 0xC0, 0xFE, 0x38, 0xE3, 0xF8, 0x00, 0x1F,
+ 0xF0, 0x0B, 0x0E, 0x00, 0xF3, 0x0B, 0x0E, 0x00, 0x06, 0x00, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c b/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c
new file mode 100644
index 000000000..5cd2f613f
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c
@@ -0,0 +1,18 @@
+// ##################
+// ##################
+// ####..........####
+// ####..........####
+// ####..##..........
+// ####......########
+// ####......##....##
+// ####......##......
+// ####......########
+// ####............##
+// ########..##....##
+// ########..########
+
+#include "images.h"
+
+const image_t img_csLogo_Small = { 9, 12, false, 14, 0, {
+ 0xFF, 0xFF, 0xF0, 0x78, 0x3D, 0x06, 0x3F, 0x13, 0x88, 0xC7, 0xE0, 0x7D, 0x3E, 0xF0
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c b/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c
new file mode 100644
index 000000000..533b79c7b
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c
@@ -0,0 +1,13 @@
+// ....##############......########
+// ....##############......########
+// ....####......####......####....
+// ....####......####......####....
+// ....####......####......####....
+// ########......##############....
+// ########......##############....
+
+#include "images.h"
+
+const image_t img_ecp_SCL = { 16, 7, false, 14, 0, {
+ 0x3F, 0x8F, 0x3F, 0x8F, 0x31, 0x8C, 0x31, 0x8C, 0x31, 0x8C, 0xF1, 0xFC, 0xF1, 0xFC
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c b/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c
new file mode 100644
index 000000000..7384fc87c
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c
@@ -0,0 +1,19 @@
+// ......##..........................
+// ....####..........................
+// ..####............................
+// ######################............
+// ######################....##......
+// ..####....................####....
+// ....####....................####..
+// ......##....######################
+// ............######################
+// ............................####..
+// ..........................####....
+// ..........................##......
+
+#include "images.h"
+
+const image_t img_ecp_SDA = { 17, 12, false, 26, 0, {
+ 0x10, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1F, 0xFC, 0x0F, 0xFE, 0x43, 0x00, 0x30, 0xC0, 0x0C, 0x27,
+ 0xFF, 0x03, 0xFF, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x80
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c b/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c
new file mode 100644
index 000000000..942f0c593
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c
@@ -0,0 +1,66 @@
+// ....................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##
+// ..................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..
+// ................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##
+// ..............##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..
+// ............##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##
+// ..........##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..
+// ........##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##
+// ......######################################################################################################################..##..##..##..
+// ......########################################################################################################################..##..##..##
+// ......####..............................................................................................................####..##..##..##..
+// ......####..............................................................................................................######..##..##..##
+// ......####..............................................................................................................####..##..##..##..
+// ......####..............................................................................................................######..##..##..##
+// ......####..............................................................................................................####..##..##..##..
+// ......####........##############################################################################################........######..##..##..##
+// ......####........##############################################################################################........####..##..##..##..
+// ......####........####........####..........####........####..........####........####..........####........####........######..##..##..##
+// ......####........####........####..........####........####..........####........####..........####........####........####..##..##..##..
+// ......####........####........##################........##################........##################........####........######..##..##..##
+// ......####........####........##################........##################........##################........####........####..##..##..##..
+// ......####........####......................................................................................####........######..##..##..##
+// ..########........####......................................................................................####........####..##########..
+// ##########........####......................................................................................####........##################
+// ##########........####......................................................................................####........####..##########..
+// ..########........####......................................................................................####........##################
+// ......####........####........##################..................................##################........####........####..##..##..##..
+// ......####........####........##################..................................##################........####........######..##..##..##
+// ......####........####........####..........####........##################........####..........####........####........####..##..##..##..
+// ......####........####........####..........####........##..............##........####..........####........####........######..##..##..##
+// ......####........##############################################################################################........####..##..##..##..
+// ......####........##############################################################################################........######..##..##..##
+// ......####..............................................................................................................####..##..##..##..
+// ......####..............................................................................................................######..##..##..##
+// ......####..............................................................................................................####..##..##..##..
+// ......####....................................######################################....................................######..##..##..##
+// ......####....................................######################################....................................####..##..##..##..
+// ......####....................................####..##..##..##..................####....................................######..##..##....
+// ......####....................................######..##..##....................####....................................####..##..##......
+// ......####....................................####..##..##......................####....................................######..##........
+// ......####....................................######..##........................####....................................####..##..........
+// ......############################################..##..........................##############################################............
+// ......##############################################............................############################################..............
+
+#include "images.h"
+
+const image_t img_ecp_port = { 69, 42, true, 290, 0x04, { // orig:363, comp:20.11%
+ 0x00, 0x2A, 0x04, 0x06, 0xAA, 0xA8, 0x02, 0x04, 0x07, 0xAA, 0x80, 0x2A, 0x04, 0x07, 0xAA, 0x02,
+ 0x04, 0x07, 0xAA, 0xA0, 0x2A, 0x04, 0x07, 0xAA, 0x82, 0x04, 0x07, 0xAA, 0xA8, 0x2A, 0x04, 0x07,
+ 0xAA, 0xA3, 0x04, 0x07, 0xFF, 0xAA, 0x1F, 0x04, 0x06, 0xFF, 0xFE, 0xA8, 0xC0, 0x04, 0x06, 0x00,
+ 0x6A, 0x86, 0x04, 0x06, 0x00, 0x03, 0xAA, 0x30, 0x04, 0x06, 0x00, 0x1A, 0xA1, 0x80, 0x04, 0x06,
+ 0x00, 0xEA, 0x8C, 0x04, 0x06, 0x00, 0x06, 0xA8, 0x61, 0x04, 0x05, 0xFF, 0xFC, 0x3A, 0xA3, 0x0F,
+ 0x04, 0x05, 0xFF, 0xE1, 0xAA, 0x18, 0x61, 0x83, 0x0C, 0x18, 0x60, 0xC3, 0x0E, 0xA8, 0xC3, 0x0C,
+ 0x18, 0x60, 0xC3, 0x06, 0x18, 0x6A, 0x86, 0x18, 0x7F, 0xC3, 0xFE, 0x1F, 0xF0, 0xC3, 0xAA, 0x30,
+ 0xC3, 0xFE, 0x1F, 0xF0, 0xFF, 0x86, 0x1A, 0xA1, 0x86, 0x04, 0x05, 0x00, 0x30, 0xEA, 0xBC, 0x30,
+ 0x04, 0x04, 0x00, 0x01, 0x86, 0xFB, 0xE1, 0x80, 0x04, 0x04, 0x00, 0x0C, 0x3F, 0xFF, 0x0C, 0x04,
+ 0x05, 0x00, 0x61, 0xBE, 0x78, 0x60, 0x04, 0x04, 0x00, 0x03, 0x0F, 0xF8, 0xC3, 0x0F, 0xF8, 0x00,
+ 0x03, 0xFE, 0x18, 0x6A, 0x86, 0x18, 0x7F, 0xC0, 0x00, 0x1F, 0xF0, 0xC3, 0xAA, 0x30, 0xC3, 0x06,
+ 0x1F, 0xF0, 0xC1, 0x86, 0x1A, 0xA1, 0x86, 0x18, 0x30, 0x80, 0x86, 0x0C, 0x30, 0xEA, 0x8C, 0x3F,
+ 0x04, 0x05, 0xFF, 0x86, 0xA8, 0x61, 0x04, 0x05, 0xFF, 0xFC, 0x3A, 0xA3, 0x04, 0x06, 0x00, 0x01,
+ 0xAA, 0x18, 0x04, 0x06, 0x00, 0x0E, 0xA8, 0xC0, 0x04, 0x06, 0x00, 0x6A, 0x86, 0x00, 0x00, 0x7F,
+ 0xFF, 0xF0, 0x00, 0x03, 0xAA, 0x30, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x1A, 0xA1, 0x80, 0x00,
+ 0x1A, 0xA0, 0x0C, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, 0xEA, 0x00, 0x60, 0x00, 0x06, 0xA0, 0x60,
+ 0x00, 0x06, 0xA0, 0x03, 0x00, 0x00, 0x3A, 0x03, 0x00, 0x00, 0x3A, 0x00, 0x18, 0x00, 0x01, 0xA0,
+ 0x1F, 0xFF, 0xFF, 0xA0, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF,
+ 0xE0, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_Back.c b/applications/plugins/wii_ec_anal/gfx/img_key_Back.c
new file mode 100644
index 000000000..d13bcf7f2
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_Back.c
@@ -0,0 +1,15 @@
+// ..##############..
+// ##################
+// ######..##########
+// ####........######
+// ######..####..####
+// ############..####
+// ########....######
+// ##################
+// ....############..
+
+#include "images.h"
+
+const image_t img_key_Back = { 9, 9, false, 11, 0, {
+ 0x7F, 0x7F, 0xFB, 0xF8, 0x7E, 0xDF, 0xEF, 0xCF, 0xFF, 0x3F, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_D.c b/applications/plugins/wii_ec_anal/gfx/img_key_D.c
new file mode 100644
index 000000000..8d182427c
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_D.c
@@ -0,0 +1,14 @@
+// ..##############..
+// ##################
+// ##################
+// ####..........####
+// ######......######
+// ########..########
+// ##################
+// ..##############..
+
+#include "images.h"
+
+const image_t img_key_D = { 9, 8, false, 9, 0, {
+ 0x7F, 0x7F, 0xFF, 0xF8, 0x3E, 0x3F, 0xBF, 0xFE, 0xFE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_L.c b/applications/plugins/wii_ec_anal/gfx/img_key_L.c
new file mode 100644
index 000000000..1fc5556b1
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_L.c
@@ -0,0 +1,15 @@
+// ..############..
+// ################
+// ########..######
+// ######....######
+// ####......######
+// ######....######
+// ########..######
+// ################
+// ..############..
+
+#include "images.h"
+
+const image_t img_key_L = { 8, 9, false, 9, 0, {
+ 0x7E, 0xFF, 0xF7, 0xE7, 0xC7, 0xE7, 0xF7, 0xFF, 0x7E
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_OK.c b/applications/plugins/wii_ec_anal/gfx/img_key_OK.c
new file mode 100644
index 000000000..ef64128f8
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_OK.c
@@ -0,0 +1,15 @@
+// ..##############..
+// ##################
+// ######......######
+// ####..........####
+// ####..........####
+// ####..........####
+// ######......######
+// ##################
+// ....############..
+
+#include "images.h"
+
+const image_t img_key_OK = { 9, 9, false, 11, 0, {
+ 0x7F, 0x7F, 0xF8, 0xF8, 0x3C, 0x1E, 0x0F, 0x8F, 0xFF, 0x3F, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c b/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c
new file mode 100644
index 000000000..595f2f431
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c
@@ -0,0 +1,15 @@
+// ..##############..
+// ####..........####
+// ##....######....##
+// ##..##########..##
+// ##..##########..##
+// ##..##########..##
+// ##....######....##
+// ####..........####
+// ..##############..
+
+#include "images.h"
+
+const image_t img_key_OKi = { 9, 9, false, 11, 0, {
+ 0x7F, 0x60, 0xE7, 0x37, 0xDB, 0xED, 0xF6, 0x73, 0x83, 0x7F, 0x00
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_R.c b/applications/plugins/wii_ec_anal/gfx/img_key_R.c
new file mode 100644
index 000000000..87cc385bc
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_R.c
@@ -0,0 +1,15 @@
+// ..############..
+// ################
+// ######..########
+// ######....######
+// ######......####
+// ######....######
+// ######..########
+// ################
+// ..############..
+
+#include "images.h"
+
+const image_t img_key_R = { 8, 9, false, 9, 0, {
+ 0x7E, 0xFF, 0xEF, 0xE7, 0xE3, 0xE7, 0xEF, 0xFF, 0x7E
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_U.c b/applications/plugins/wii_ec_anal/gfx/img_key_U.c
new file mode 100644
index 000000000..aca5bb62a
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_U.c
@@ -0,0 +1,14 @@
+// ..##############..
+// ##################
+// ########..########
+// ######......######
+// ####..........####
+// ##################
+// ##################
+// ..##############..
+
+#include "images.h"
+
+const image_t img_key_U = { 9, 8, false, 9, 0, {
+ 0x7F, 0x7F, 0xFD, 0xFC, 0x7C, 0x1F, 0xFF, 0xFE, 0xFE
+}};
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c b/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c
new file mode 100644
index 000000000..b740780ad
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c
@@ -0,0 +1,14 @@
+// ..##############..
+// ####..........####
+// ##......##......##
+// ##....######....##
+// ##..##########..##
+// ##..............##
+// ####..........####
+// ..##############..
+
+#include "images.h"
+
+const image_t img_key_Ui = { 9, 8, false, 9, 0, {
+ 0x7F, 0x60, 0xE2, 0x33, 0x9B, 0xEC, 0x07, 0x06, 0xFE
+}};
diff --git a/applications/plugins/wii_ec_anal/i2c_workaround.h b/applications/plugins/wii_ec_anal/i2c_workaround.h
new file mode 100644
index 000000000..de1dbba54
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/i2c_workaround.h
@@ -0,0 +1,119 @@
+/*
+ As of the date of releasing this code, there is (seemingly) a bug in the FZ i2c library code
+ It is described here: https://github.com/flipperdevices/flipperzero-firmware/issues/1670
+
+ This is a short-term workaround so I can keep developing while we get to the bottom of the issue
+
+ FYI. *something* in the following code is the fix
+
+void furi_hal_i2c_acquire (FuriHalI2cBusHandle* handle)
+{
+ // 1. Disable the power/backlight (it uses i2c)
+ furi_hal_power_insomnia_enter();
+ // 2. Lock bus access
+ handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
+ // 3. Ensuree that no active handle set
+ furi_check(handle->bus->current_handle == NULL);
+ // 4. Set current handle
+ handle->bus->current_handle = handle;
+ // 5. Activate bus
+ handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate);
+ // 6. Activate handle
+ handle->callback(handle, FuriHalI2cBusHandleEventActivate);
+}
+
+void furi_hal_i2c_release (FuriHalI2cBusHandle* handle)
+{
+ // Ensure that current handle is our handle
+ furi_check(handle->bus->current_handle == handle);
+ // 6. Deactivate handle
+ handle->callback(handle, FuriHalI2cBusHandleEventDeactivate);
+ // 5. Deactivate bus
+ handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate);
+ // 3,4. Reset current handle
+ handle->bus->current_handle = NULL;
+ // 2. Unlock bus
+ handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
+ // 1. Re-enable the power system
+ furi_hal_power_insomnia_exit();
+}
+
+*/
+
+#ifndef I2C_WORKAROUND_H_
+#define I2C_WORKAROUND_H_
+
+#include
+
+#define ENABLE_WORKAROUND 1
+
+#if ENABLE_WORKAROUND == 1
+ //+============================================================================ ========================================
+ static inline
+ bool furi_hal_Wi2c_is_device_ready (FuriHalI2cBusHandle* const bus, const uint8_t addr, const uint32_t tmo)
+ {
+ furi_hal_i2c_acquire(bus);
+ bool rv = furi_hal_i2c_is_device_ready(bus, addr, tmo);
+ furi_hal_i2c_release(bus);
+ return rv;
+ }
+
+ //+============================================================================
+ static inline
+ bool furi_hal_Wi2c_tx ( FuriHalI2cBusHandle* const bus, const uint8_t addr,
+ const void* buf, const size_t len, const uint32_t tmo )
+ {
+ furi_hal_i2c_acquire(bus);
+ bool rv = furi_hal_i2c_tx(bus, addr, buf, len, tmo);
+ furi_hal_i2c_release(bus);
+ return rv;
+ }
+
+ //+============================================================================
+ static inline
+ bool furi_hal_Wi2c_rx ( FuriHalI2cBusHandle* const bus, const uint8_t addr,
+ void* buf, const size_t len, const uint32_t tmo )
+ {
+ furi_hal_i2c_acquire(bus);
+ bool rv = furi_hal_i2c_rx(bus, addr, buf, len, tmo);
+ furi_hal_i2c_release(bus);
+ return rv;
+ }
+
+ //+============================================================================
+ static inline
+ bool furi_hal_Wi2c_trx ( FuriHalI2cBusHandle* const bus, const uint8_t addr,
+ const void* tx, const size_t txlen,
+ void* rx, const size_t rxlen, const uint32_t tmo )
+ {
+ bool rv = furi_hal_Wi2c_tx(bus, addr, tx, txlen, tmo);
+ if (rv) rv = furi_hal_Wi2c_rx(bus, addr, rx, rxlen, tmo);
+ return rv;
+ }
+
+ //----------------------------------------------------------------------------- ----------------------------------------
+# define furi_hal_i2c_is_device_ready(...) furi_hal_Wi2c_is_device_ready(__VA_ARGS__)
+# define furi_hal_i2c_tx(...) furi_hal_Wi2c_tx(__VA_ARGS__)
+# define furi_hal_i2c_rx(...) furi_hal_Wi2c_rx(__VA_ARGS__)
+# define furi_hal_i2c_trx(...) furi_hal_Wi2c_trx(__VA_ARGS__)
+
+#endif //ENABLE_WORKAROUND
+
+//+============================================================================ ========================================
+// Some devices take a moment to respond to read requests
+// The puts a delay between the address being set and the data being read
+//
+static inline
+bool furi_hal_i2c_trxd ( FuriHalI2cBusHandle* const bus, const uint8_t addr,
+ const void* tx, const size_t txlen,
+ void* rx, const size_t rxlen, const uint32_t tmo, const uint32_t us )
+{
+ bool rv = furi_hal_i2c_tx(bus, addr, tx, txlen, tmo);
+ if (rv) {
+ furi_delay_us(us);
+ rv = furi_hal_i2c_rx(bus, addr, rx, rxlen, tmo);
+ }
+ return rv;
+}
+
+#endif //I2C_WORKAROUND_H_
diff --git a/applications/plugins/wii_ec_anal/info.sh b/applications/plugins/wii_ec_anal/info.sh
new file mode 100644
index 000000000..e009eb118
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/info.sh
@@ -0,0 +1,11 @@
+echo "MARKED AS TODO"
+echo "=============="
+grep //! *.c *.h
+
+echo -e "\nSUPPORTED CONTROLLERS"
+echo "====================="
+grep '\[PID_.*{ {' wii_ec.c | head -n -3 | sed 's/\s*\(.*\)/\1/'
+
+echo -e "\nLOGGING"
+echo "======="
+grep LOG_LEVEL *.h | grep -v '#if '
diff --git a/applications/plugins/wii_ec_anal/notes.txt b/applications/plugins/wii_ec_anal/notes.txt
new file mode 100644
index 000000000..61b6e29af
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/notes.txt
@@ -0,0 +1,87 @@
+//+============================================================================ ========================================
+// Select font
+// A full list of u8g2 fonts can be found here:
+// https://github.com/olikraus/u8g2/wiki/fntlistall
+// ...and here are the ones available in FZ (currently: all of them):
+// grep -P '.*u8g2.*\[[0-9]*\]' lib/u8g2/u8g2_fonts.c | sed 's/.*\(u8g2_.*\)\[.*/\1/'
+//
+#if 0 //! Extra fonts is just too memory hungry
+#include
+void setFont (Canvas* const canvas, const uint8_t* font)
+{
+ u8g2_SetFontMode(&canvas->fb, 1); // no idea - but canvas.c does it
+ u8g2_SetFont(&canvas->fb, font);
+}
+#endif
+
+litui : @BlueChip for posterity, the function to break at is flipper_application_spawn. At that point, you can set new breakpoints in your fap code and continue.
+
+/*
+
+This is wrong on quite a few levels!
+https://training.ti.com/introduction-i2c-reserved-addresses
+
+void doit (void)
+{
+ furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
+ printf("Scanning external i2c on PC0(SCL)/PC1(SDA)\r\n"
+ "Clock: 100khz, 7bit address\r\n"
+ "\r\n");
+ printf(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n");
+ printf("--+--------------------------------\r\n");
+ for(uint8_t row = 0; row < 0x8; row++) {
+ printf("%x | ", row);
+ for(uint8_t column = 0; column <= 0xF; column++) {
+ bool ret = furi_hal_i2c_is_device_ready(
+ &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2);
+ printf("%c ", ret ? '#' : '-');
+ }
+ printf("\r\n");
+ }
+ furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+}
+*/
+
+
+region locking : firmware/targets/f7/furi_hal/furi_hal_region.c
+
+
+# if 0 //! scrolling works beautifully, but the LCD refresh can't keep up :(
+ // Waveform
+ if (cnt) { // start
+ for (int a = ACC_1; a < ACC_N; a++) {
+ canvas_draw_dot(canvas, x,y[a]+v[a][idx]);
+ for (int i = 1; i < aw -cnt; i++) {
+ canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]);
+ }
+ }
+ } else { // scroll
+ for (int a = ACC_1; a < ACC_N; a++) {
+ for (int i = 0; i < aw; i++) {
+ int off = (idx +i) %aw;
+ int prev = off ? off-1 : aw-1;
+ canvas_draw_line(canvas, x+i,y[a]+v[a][prev] , x+i,y[a]+v[a][off]);
+ }
+ }
+ }
+
+# else
+ int end = idx ? idx : aw;
+ for (int a = ACC_1; a < ACC_N; a++) {
+ canvas_draw_dot(canvas, x,y[a]+v[a][idx]);
+ if (state->apause) {
+ for (int i = 1; i < end; i++)
+ canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]);
+ } else {
+ for (int i = 1; i < end; i++)
+ canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]);
+ for (int i = end+10; i < aw -cnt; i++)
+ canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]);
+ }
+ }
+
+ // Wipe bar
+ if (end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1);
+ if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1);
+ if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1);
+# endif
diff --git a/applications/plugins/wii_ec_anal/wii_anal.c b/applications/plugins/wii_ec_anal/wii_anal.c
new file mode 100644
index 000000000..fdf718b63
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal.c
@@ -0,0 +1,540 @@
+//----------------------------------------------------------------------------- ----------------------------------------
+// Includes
+//
+
+// System libs
+#include // malloc
+#include // uint32_t
+#include // __VA_ARGS__
+#include
+#include
+
+// FlipperZero libs
+#include // Core API
+#include // GUI (screen/keyboard) API
+#include // GUI Input extensions
+#include
+
+// Do this first!
+#define ERR_C_ // Do this in precisely ONE file
+#include "err.h" // Error numbers & messages
+
+#include "bc_logging.h"
+
+// Local headers
+#include "wii_anal.h" // Various enums and struct declarations
+#include "wii_i2c.h" // Wii i2c functions
+#include "wii_ec.h" // Wii Extension Controller functions (eg. draw)
+#include "wii_anal_keys.h" // key mappings
+#include "gfx/images.h" // Images
+#include "wii_anal_lcd.h" // Drawing functions
+#include "wii_anal_ec.h" // Wii controller events
+
+#include "wii_anal_ver.h" // Version number
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// OOOOO // SSSSS CCCCC AAA L L BBBB AAA CCCC K K SSSSS
+// O O /// S C A A L L B B A A C K K S
+// O O /// SSSSS C AAAAA L L BBBB AAAAA C KKK SSSSS
+// O O /// S C A A L L B B A A C K K S
+// OOOOO // SSSSS CCCCC A A LLLLL LLLLL BBBB A A CCCC K K SSSSS
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//+============================================================================ ========================================
+// OS Callback : Timer tick
+// We register this function to be called when the OS signals a timer 'tick' event
+//
+static
+void cbTimer (FuriMessageQueue* queue)
+{
+ ENTER;
+ furi_assert(queue);
+
+ eventMsg_t message = {.id = EVID_TICK};
+ furi_message_queue_put(queue, &message, 0);
+
+ LEAVE;
+ return;
+}
+
+//+============================================================================ ========================================
+// OS Callback : Keypress
+// We register this function to be called when the OS detects a keypress
+//
+static
+void cbInput (InputEvent* event, FuriMessageQueue* queue)
+{
+ ENTER;
+ furi_assert(queue);
+ furi_assert(event);
+
+ // Put an "input" event message on the message queue
+ eventMsg_t message = {.id = EVID_KEY, .input = *event};
+ furi_message_queue_put(queue, &message, FuriWaitForever);
+
+ LEAVE;
+ return;
+}
+
+//+============================================================================
+// Show version number
+//
+static
+void showVer (Canvas* const canvas)
+{
+ show(canvas, 0,59, &img_3x5_v, SHOW_SET_BLK);
+ show(canvas, 4,59, VER_MAJ, SHOW_SET_BLK);
+ canvas_draw_frame(canvas, 8,62, 2,2);
+ show(canvas, 11,59, VER_MIN, SHOW_SET_BLK);
+}
+
+//+============================================================================
+// OS Callback : Draw request
+// We register this function to be called when the OS requests that the screen is redrawn
+//
+// We actually instruct the OS to perform this request, after we update the interface
+// I guess it's possible that this instruction may able be issued by other threads !?
+//
+static
+void cbDraw (Canvas* const canvas, void* ctx)
+{
+ ENTER;
+ furi_assert(canvas);
+ furi_assert(ctx);
+
+ state_t* state = NULL;
+
+ // Try to acquire the mutex for the plugin state variables, timeout = 25mS
+ if ( !(state = (state_t*)acquire_mutex((ValueMutex*)ctx, 25)) ) return ;
+
+ switch (state->scene) {
+ //---------------------------------------------------------------------
+ case SCENE_SPLASH:
+ show(canvas, 2,0, &img_csLogo_FULL, SHOW_SET_BLK);
+
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 64,43, AlignCenter, AlignTop, "Wii Extension Controller");
+ canvas_draw_str_aligned(canvas, 64,55, AlignCenter, AlignTop, "Protocol Analyser");
+
+ showVer(canvas);
+
+ break;
+
+ //---------------------------------------------------------------------
+ case SCENE_RIP:
+ show(canvas, 0,0, &img_RIP, SHOW_SET_BLK);
+ break;
+
+ //---------------------------------------------------------------------
+ case SCENE_WAIT:
+# define xo 2
+
+ show(canvas, 3+xo,10, &img_ecp_port, SHOW_SET_BLK);
+
+ BOX_TL(22+xo, 6, 82+xo,23); // 3v3
+ BOX_TL(48+xo,21, 82+xo,23); // C1
+ BOX_BL(22+xo,41, 82+xo,58); // C0
+ BOX_BL(48+xo,41, 82+xo,44); // Gnd
+
+ show(canvas, 90+xo, 3, &img_6x8_3, SHOW_SET_BLK); // 3v3
+ show(canvas, 97+xo, 3, &img_6x8_v, SHOW_SET_BLK);
+ show(canvas, 104+xo, 3, &img_6x8_3, SHOW_SET_BLK);
+
+ show(canvas, 90+xo,18, &img_6x8_C, SHOW_SET_BLK); // C1 <->
+ show(canvas, 98+xo,18, &img_6x8_1, SHOW_SET_BLK);
+ show(canvas, 107+xo,16, &img_ecp_SDA, SHOW_SET_BLK);
+
+ show(canvas, 90+xo,40, &img_6x8_G, SHOW_SET_BLK); // Gnd
+ show(canvas, 97+xo,40, &img_6x8_n, SHOW_SET_BLK);
+ show(canvas, 104+xo,40, &img_6x8_d, SHOW_SET_BLK);
+
+ show(canvas, 90+xo,54, &img_6x8_C, SHOW_SET_BLK); // C0 _-_-
+ show(canvas, 98+xo,54, &img_6x8_0, SHOW_SET_BLK);
+ show(canvas, 108+xo,54, &img_ecp_SCL, SHOW_SET_BLK);
+
+ show(canvas, 0,0, &img_csLogo_Small, SHOW_SET_BLK);
+ showVer(canvas);
+
+# undef xo
+ break;
+
+ //---------------------------------------------------------------------
+ case SCENE_DEBUG:
+ canvas_set_font(canvas, FontSecondary);
+
+ show(canvas, 0,0, &img_key_U, SHOW_SET_BLK);
+ canvas_draw_str_aligned(canvas, 11, 0, AlignLeft, AlignTop, "Initialise Perhipheral");
+
+ show(canvas, 0,11, &img_key_OK, SHOW_SET_BLK);
+ canvas_draw_str_aligned(canvas, 11,11, AlignLeft, AlignTop, "Read values [see log]");
+
+ show(canvas, 0,23, &img_key_D, SHOW_SET_BLK);
+ canvas_draw_str_aligned(canvas, 11,22, AlignLeft, AlignTop, "Restart Scanner");
+
+ show(canvas, 0,33, &img_key_Back, SHOW_SET_BLK);
+ canvas_draw_str_aligned(canvas, 11,33, AlignLeft, AlignTop, "Exit");
+
+ break ;
+
+ //---------------------------------------------------------------------
+ default:
+ if (state->ec.pidx >= PID_ERROR) {
+ ERROR("%s : bad PID = %d", __func__, state->ec.pidx);
+ } else {
+ if ((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].show)
+ ecId[PID_UNKNOWN].show(canvas, state);
+ else
+ ecId[state->ec.pidx].show(canvas, state);
+ }
+ break;
+ }
+
+ // Release the mutex
+ release_mutex((ValueMutex*)ctx, state);
+
+ LEAVE;
+ return;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// SSSSS TTTTT AAA TTTTT EEEEE V V AAA RRRR IIIII AAA BBBB L EEEEE SSSSS
+// S T A A T E V V A A R R I A A B B L E S
+// SSSSS T AAAAA T EEE V V AAAAA RRRR I AAAAA BBBB L EEE SSSSS
+// S T A A T E V V A A R R I A A B B L E S
+// SSSSS T A A T EEEEE V A A R R IIIII A A BBBB LLLLL EEEEE SSSSS
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//+============================================================================ ========================================
+// Initialise plugin state variables
+//
+static inline
+bool stateInit (state_t* const state)
+{
+ ENTER;
+ furi_assert(state);
+
+ bool rv = true; // assume success
+
+ // Enable the main loop
+ state->run = true;
+
+ // Timer
+ state->timerEn = false;
+ state->timer = NULL;
+ state->timerHz = furi_kernel_get_tick_frequency();
+ state->fps = 30;
+
+ // Scene
+ state->scene = SCENE_SPLASH;
+ state->scenePrev = SCENE_NONE;
+ state->scenePegg = SCENE_NONE;
+
+ state->hold = 0; // show hold meters (-1=lowest, 0=current, +1=highest}
+ state->calib = CAL_TRACK;
+ state->pause = false; // animation running
+ state->apause = false; // auto-pause animation
+
+ // Notifications
+ state->notify = NULL;
+
+ // Perhipheral
+ state->ec.init = false;
+ state->ec.pidx = PID_UNKNOWN;
+ state->ec.sid = ecId[state->ec.pidx].name;
+
+ // Controller data
+ memset(state->ec.pid, 0xC5, PID_LEN); // Cyborg 5ystems
+ memset(state->ec.calF, 0xC5, CAL_LEN);
+ memset(state->ec.joy, 0xC5, JOY_LEN);
+
+ // Encryption details
+ state->ec.encrypt = false;
+ memset(state->ec.encKey, 0x00, ENC_LEN);
+
+ // Seed the PRNG
+ // CYCCNT --> lib/STM32CubeWB/Drivers/CMSIS/Include/core_cm7.h
+// srand(DWT->CYCCNT);
+
+ LEAVE;
+ return rv;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// MM MM AAA IIIII N N
+// M M M A A I NN N
+// M M M AAAAA I N N N
+// M M A A I N NN
+// M M A A IIIII N N
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//+============================================================================ ========================================
+// Enable/Disable scanning
+//
+void timerEn (state_t* state, bool on)
+{
+ ENTER;
+ furi_assert(state);
+
+ // ENable scanning
+ if (on) {
+ if (state->timerEn) {
+ WARN(wii_errs[WARN_SCAN_START]);
+ } else {
+ // Set the timer to fire at 'fps' times/second
+ if (furi_timer_start(state->timer, state->timerHz/state->fps) == FuriStatusOk) {
+ state->timerEn = true;
+ INFO("%s : monitor started", __func__);
+ } else {
+ ERROR(wii_errs[ERR_TIMER_START]);
+ }
+ }
+
+ // DISable scanning
+ } else {
+ if (!state->timerEn) {
+ WARN(wii_errs[WARN_SCAN_STOP]);
+ } else {
+ // Stop the timer
+ if (furi_timer_stop(state->timer) == FuriStatusOk) {
+ state->timerEn = false;
+ INFO("%s : monitor stopped", __func__);
+ } else {
+ ERROR(wii_errs[ERR_TIMER_STOP]);
+ }
+ }
+ }
+
+ LEAVE;
+ return;
+}
+
+//+============================================================================ ========================================
+// Plugin entry point
+//
+int32_t wii_ec_anal (void)
+{
+ ENTER;
+
+ // ===== Variables =====
+ err_t error = 0; // assume success
+ Gui* gui = NULL;
+ ViewPort* vpp = NULL;
+ state_t* state = NULL;
+ ValueMutex mutex = {0};
+ FuriMessageQueue* queue = NULL;
+ const uint32_t queueSz = 20; // maximum messages in queue
+ uint32_t tmo = (3.5f *1000); // timeout splash screen after N seconds
+
+ // The queue will contain plugin event-messages
+ // --> local
+ eventMsg_t msg = {0};
+
+ INFO("BEGIN");
+
+ // ===== Message queue =====
+ // 1. Create a message queue (for up to 8 (keyboard) event messages)
+ if ( !(queue = furi_message_queue_alloc(queueSz, sizeof(msg))) ) {
+ ERROR(wii_errs[(error = ERR_MALLOC_QUEUE)]);
+ goto bail;
+ }
+
+ // ===== Create GUI Interface =====
+ // 2. Create a GUI interface
+ if ( !(gui = furi_record_open("gui")) ) {
+ ERROR(wii_errs[(error = ERR_NO_GUI)]);
+ goto bail;
+ }
+
+ // ===== Plugin state variables =====
+ // 3. Allocate space on the heap for the plugin state variables
+ if ( !(state = malloc(sizeof(state_t))) ) {
+ ERROR(wii_errs[(error = ERR_MALLOC_STATE)]);
+ goto bail;
+ }
+ // 4. Initialise the plugin state variables
+ if (!stateInit(state)) {
+ // error message(s) is/are output by stateInit()
+ error = 15;
+ goto bail;
+ }
+ // 5. Create a mutex for (reading/writing) the plugin state variables
+ if (!init_mutex(&mutex, state, sizeof(state))) {
+ ERROR(wii_errs[(error = ERR_NO_MUTEX)]);
+ goto bail;
+ }
+
+ // ===== Viewport =====
+ // 6. Allocate space on the heap for the viewport
+ if ( !(vpp = view_port_alloc()) ) {
+ ERROR(wii_errs[(error = ERR_MALLOC_VIEW)]);
+ goto bail;
+ }
+ // 7a. Register a callback for input events
+ view_port_input_callback_set(vpp, cbInput, queue);
+ // 7b. Register a callback for draw events
+ view_port_draw_callback_set(vpp, cbDraw, &mutex);
+
+ // ===== Start GUI Interface =====
+ // 8. Attach the viewport to the GUI
+ gui_add_view_port(gui, vpp, GuiLayerFullscreen);
+
+ // ===== Timer =====
+ // 9. Allocate a timer
+ if ( !(state->timer = furi_timer_alloc(cbTimer, FuriTimerTypePeriodic, queue)) ) {
+ ERROR(wii_errs[(error = ERR_NO_TIMER)]);
+ goto bail;
+ }
+
+ // === System Notifications ===
+ // 10. Acquire a handle for the system notification queue
+ if ( !(state->notify = furi_record_open(RECORD_NOTIFICATION)) ) {
+ ERROR(wii_errs[(error = ERR_NO_NOTIFY)]);
+ goto bail;
+ }
+ patBacklight(state); // Turn on the backlight [qv. remote FAP launch]
+
+ INFO("INITIALISED");
+
+ // ==================== Main event loop ====================
+
+ if (state->run) do {
+ bool redraw = false;
+ FuriStatus status = FuriStatusErrorTimeout;
+
+ // Wait for a message
+// while ((status = furi_message_queue_get(queue, &msg, tmo)) == FuriStatusErrorTimeout) ;
+ status = furi_message_queue_get(queue, &msg, tmo);
+
+ // Clear splash screen
+ if ( (state->scene == SCENE_SPLASH) && (state->scenePrev == SCENE_NONE) && // Initial splash
+ ( (status == FuriStatusErrorTimeout) || // timeout
+ ((msg.id == EVID_KEY) && (msg.input.type == InputTypeShort)) ) // or key-short
+ ) {
+ tmo = 60 *1000; // increase message-wait timeout to 60secs
+ timerEn(state, true); // start scanning the i2c bus
+ status = FuriStatusOk; // pass status check
+ msg.id = EVID_NONE; // valid msg ID
+ sceneSet(state, SCENE_WAIT); // move to wait screen
+ }
+
+ // Check for queue errors
+ if (status != FuriStatusOk) {
+ switch (status) {
+ case FuriStatusErrorTimeout: DEBUG(wii_errs[DEBUG_QUEUE_TIMEOUT]); continue ;
+ case FuriStatusError: ERROR(wii_errs[(error = ERR_QUEUE_RTOS)]); goto bail ;
+ case FuriStatusErrorResource: ERROR(wii_errs[(error = ERR_QUEUE_RESOURCE)]); goto bail ;
+ case FuriStatusErrorParameter: ERROR(wii_errs[(error = ERR_QUEUE_BADPRM)]); goto bail ;
+ case FuriStatusErrorNoMemory: ERROR(wii_errs[(error = ERR_QUEUE_NOMEM)]); goto bail ;
+ case FuriStatusErrorISR: ERROR(wii_errs[(error = ERR_QUEUE_ISR)]); goto bail ;
+ default: ERROR(wii_errs[(error = ERR_QUEUE_UNK)]); goto bail ;
+ }
+ }
+ // Read successful
+
+ // *** Try to lock the plugin state variables ***
+ if ( !(state = (state_t*)acquire_mutex_block(&mutex)) ) {
+ ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]);
+ goto bail;
+ }
+
+ // *** Handle events ***
+ switch (msg.id) {
+ //---------------------------------------------
+ case EVID_TICK: // Timer events
+ //! I would prefer to have ecPoll() called by cbTimer()
+ //! ...but how does cbTimer() get the required access to the state variables? Namely: 'state->ec'
+ //! So, for now, the timer pushes a message to call ecPoll()
+ //! which, in turn, will push WIIEC event meesages!
+ ecPoll(&state->ec, queue);
+ break;
+
+ //---------------------------------------------
+ case EVID_WIIEC: // WiiMote Perhipheral
+ if (evWiiEC(&msg, state)) redraw = true ;
+ break;
+
+ //---------------------------------------------
+ case EVID_KEY: // Key events
+ patBacklight(state);
+ if (evKey(&msg, state)) redraw = true;
+ break;
+
+ //---------------------------------------------
+ case EVID_NONE:
+ break;
+
+ //---------------------------------------------
+ default: // Unknown event
+ WARN("Unknown message.ID [%d]", msg.id);
+ break;
+ }
+
+ // *** Update the GUI screen via the viewport ***
+ if (redraw) view_port_update(vpp) ;
+
+ // *** Try to release the plugin state variables ***
+ if ( !release_mutex(&mutex, state) ) {
+ ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]);
+ goto bail;
+ }
+ } while (state->run);
+
+ // ===== Game Over =====
+ INFO("USER EXIT");
+
+bail:
+ // 10. Release system notification queue
+ if (state->notify) {
+ furi_record_close(RECORD_NOTIFICATION);
+ state->notify = NULL;
+ }
+
+ // 9. Stop the timer
+ if (state->timer) {
+ (void)furi_timer_stop(state->timer);
+ furi_timer_free(state->timer);
+ state->timer = NULL;
+ state->timerEn = false;
+ }
+
+ // 8. Detach the viewport
+ gui_remove_view_port(gui, vpp);
+
+ // 7. No need to unreqgister the callbacks
+ // ...they will go when the viewport is destroyed
+
+ // 6. Destroy the viewport
+ if (vpp) {
+ view_port_enabled_set(vpp, false);
+ view_port_free(vpp);
+ vpp = NULL;
+ }
+
+ // 5. Free the mutex
+ if (mutex.mutex) {
+ delete_mutex(&mutex);
+ mutex.mutex = NULL;
+ }
+
+ // 4. Free up state pointer(s)
+ // none
+
+ // 3. Free the plugin state variables
+ if (state) {
+ free(state);
+ state = NULL;
+ }
+
+ // 2. Close the GUI
+ furi_record_close("gui");
+
+ // 1. Destroy the message queue
+ if (queue) {
+ furi_message_queue_free(queue);
+ queue = NULL;
+ }
+
+ INFO("CLEAN EXIT ... Exit code: %d", error);
+ LEAVE;
+ return (int32_t)error;
+}
diff --git a/applications/plugins/wii_ec_anal/wii_anal.h b/applications/plugins/wii_ec_anal/wii_anal.h
new file mode 100644
index 000000000..ac7ffddb1
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal.h
@@ -0,0 +1,97 @@
+#ifndef WII_ANAL_H_
+#define WII_ANAL_H_
+
+#include // Core API
+#include // GUI Input extensions
+#include
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// GUI scenes
+//
+typedef
+ enum scene {
+ SCENE_NONE = 0,
+ SCENE_SPLASH = 1,
+ SCENE_RIP = 2,
+ SCENE_WAIT = 3,
+ SCENE_DEBUG = 4,
+ SCENE_DUMP = 5,
+ SCENE_CLASSIC = 6,
+ SCENE_CLASSIC_N = 7,
+ SCENE_NUNCHUCK = 8,
+ SCENE_NUNCHUCK_ACC = 9,
+ }
+scene_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+#include "wii_i2c.h"
+#include "wii_ec.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// A list of event IDs handled by this plugin
+//
+typedef
+ enum eventID {
+ EVID_NONE,
+ EVID_UNKNOWN,
+
+ // A full list of events can be found with: `grep -r --color "void.*set_.*_callback" applications/gui/*`
+ // ...A free gift to you from the makers of well written code that conforms to a good coding standard
+ EVID_KEY, // keypad
+ EVID_TICK, // tick
+ EVID_WIIEC, // wii extension controller
+ }
+eventID_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// An item in the event message-queue
+//
+typedef
+ struct eventMsg {
+ eventID_t id;
+ union {
+ InputEvent input; // --> applications/input/input.h
+ wiiEcEvent_t wiiEc; // --> local
+ };
+ }
+eventMsg_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// State variables for this plugin
+// An instance of this is allocated on the heap, and the pointer is passed back to the OS
+// Access to this memory is controlled by mutex
+//
+typedef
+ struct state {
+ bool run; // true : plugin is running
+
+ bool timerEn; // controller scanning enabled
+ FuriTimer* timer; // the timer
+ uint32_t timerHz; // system ticks per second
+ int fps; // poll/refresh [frames]-per-second
+
+ int cnvW; // canvas width
+ int cnvH; // canvas height
+ scene_t scene; // current scene
+ scene_t scenePrev; // previous scene
+ scene_t scenePegg; // previous scene for easter eggs
+ int flash; // flash counter (flashing icons)
+
+ int hold; // hold type: {-1=tough-peak, 0=none, +1=peak-hold}
+ ecCalib_t calib; // Software calibration mode
+
+ bool pause; // Accelerometer animation pause
+ bool apause; // Accelerometer animation auto-pause
+
+ NotificationApp* notify; // OS nitifcation queue (for patting the backlight watchdog timer)
+
+ wiiEC_t ec; // Extension Controller details
+ }
+state_t;
+
+//============================================================================= ========================================
+// Function prototypes
+//
+void timerEn (state_t* state, bool on) ;
+
+#endif //WII_ANAL_H_
diff --git a/applications/plugins/wii_ec_anal/wii_anal_ec.c b/applications/plugins/wii_ec_anal/wii_anal_ec.c
new file mode 100644
index 000000000..94f021289
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_ec.c
@@ -0,0 +1,85 @@
+#include
+#include
+
+#include "wii_anal.h"
+#include "wii_anal_lcd.h"
+#include "wii_anal_keys.h"
+
+//+============================================================================ ========================================
+// Handle Wii Extension Controller events
+//
+bool evWiiEC (const eventMsg_t* const msg, state_t* const state)
+{
+ bool redraw = false;
+
+# if LOG_LEVEL >= 4
+ {
+ const char* s = NULL;
+ switch (msg->wiiEc.type) {
+ case WIIEC_NONE: s = "Error"; break ;
+ case WIIEC_CONN: s = "Connect"; break ;
+ case WIIEC_DISCONN: s = "Disconnect"; break ;
+ case WIIEC_PRESS: s = "Press"; break ;
+ case WIIEC_RELEASE: s = "Release"; break ;
+ case WIIEC_ANALOG: s = "Analog"; break ;
+ case WIIEC_ACCEL: s = "Accel"; break ;
+ default: s = "Bug"; break ;
+ }
+ INFO("WIIP : %s '%c' = %d", s, (isprint((int)msg->wiiEc.in) ? msg->wiiEc.in : '_'), msg->wiiEc.val);
+ if ((msg->wiiEc.type == WIIEC_CONN) || (msg->wiiEc.type == WIIEC_DISCONN))
+ INFO("...%d=\"%s\"", msg->wiiEc.val, ecId[msg->wiiEc.val].name);
+ }
+# endif
+
+ switch (msg->wiiEc.type) {
+ case WIIEC_CONN:
+ patBacklight(state);
+ state->hold = 0;
+ state->calib = CAL_TRACK;
+ sceneSet(state, ecId[msg->wiiEc.val].scene);
+ redraw = true ;
+ break;
+
+ case WIIEC_DISCONN:
+ patBacklight(state);
+ sceneSet(state, SCENE_WAIT);
+ redraw = true;
+ break;
+
+ case WIIEC_PRESS:
+ if (state->scene == SCENE_NUNCHUCK_ACC) switch (msg->wiiEc.in) {
+ case 'z': // un-pause
+ state->pause = !state->pause;
+ break;
+ case 'c': // toggle auto-pause
+ state->pause = false;
+ state->apause = !state->apause;
+ break;
+ default: break ;
+ }
+
+#if 1 //! factory calibration method not known for classic triggers - this will set the digital switch point
+ if (state->ec.pidx == PID_CLASSIC) {
+ if (msg->wiiEc.in == 'l') state->ec.calS.classic[2].trgZL = msg->wiiEc.val ;
+ if (msg->wiiEc.in == 'r') state->ec.calS.classic[2].trgZR = msg->wiiEc.val ;
+ }
+#endif
+ __attribute__ ((fallthrough));
+
+ case WIIEC_RELEASE:
+ patBacklight(state);
+ redraw = true;
+ break;
+
+ case WIIEC_ANALOG:
+ case WIIEC_ACCEL:
+ ecCalibrate(&state->ec, state->calib);
+ redraw = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return redraw;
+}
diff --git a/applications/plugins/wii_ec_anal/wii_anal_ec.h b/applications/plugins/wii_ec_anal/wii_anal_ec.h
new file mode 100644
index 000000000..886b60281
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_ec.h
@@ -0,0 +1,14 @@
+#ifndef WII_ANAL_EC_H_
+#define WII_ANAL_EC_H_
+
+#include
+
+//============================================================================= ========================================
+// Function prototypes
+//
+typedef struct eventMsg eventMsg_t ;
+typedef struct state state_t ;
+
+bool evWiiEC (const eventMsg_t* const msg, state_t* const state) ;
+
+#endif //WII_ANAL_EC_H_
diff --git a/applications/plugins/wii_ec_anal/wii_anal_keys.c b/applications/plugins/wii_ec_anal/wii_anal_keys.c
new file mode 100644
index 000000000..bcdcf5a62
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_keys.c
@@ -0,0 +1,295 @@
+#include
+
+#include "bc_logging.h"
+
+#include "wii_anal.h"
+
+//+============================================================================ ========================================
+// Stop Calibration mode
+//
+static
+void calStop (state_t* const state)
+{
+ state->hold = 0; // stop calibration mode
+ state->calib &= ~(CAL_RANGE | CAL_NOTJOY); // ...
+}
+
+//+============================================================================ ========================================
+// Change to another scene
+//
+void sceneSet (state_t* const state, const scene_t scene)
+{
+ calStop(state); // Stop software calibration
+ state->scenePrev = state->scene; // Remember where we came from
+ state->scene = scene; // Go to new scene
+ INFO("Scene : %d -> %d", state->scenePrev, state->scene);
+}
+
+//+============================================================================ ========================================
+// Change to an easter egg scene
+//
+static
+void sceneSetEgg (state_t* const state, const scene_t scene)
+{
+ calStop(state); // Stop software calibration
+ state->scenePegg = state->scene; // Remember where we came from
+ state->scene = scene; // Go to new scene
+ INFO("Scene* : %d => %d", state->scenePegg, state->scene);
+}
+
+//+============================================================================ ========================================
+// Several EC screens have 'peak-hold' and 'calibration' features
+// Enabling peak-hold on screen with no peak meters will have no effect
+// So, to avoid code duplication...
+//
+bool key_calib (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ switch (msg->input.type) {
+ case InputTypeShort: //# input.key) {
+ case InputKeyUp: //# hold = (state->hold == +1) ? 0 : +1 ; // toggle peak hold
+ used = true;
+ break;
+
+ case InputKeyDown: //# hold = (state->hold == -1) ? 0 : -1 ; // toggle trough hold
+ used = true;
+ break;
+
+ case InputKeyOk: //# calib & CAL_RANGE) calStop(state) ; // STOP softare calibration
+ else ecCalibrate(&state->ec, CAL_CENTRE) ; // perform centre calibration
+ used = true;
+ break;
+
+ default: break ;
+ }
+ break;
+
+ case InputTypeLong: //# >! After INPUT_LONG_PRESS interval, asynch to InputTypeRelease
+ switch (msg->input.key) {
+ case InputKeyOk: //# >O [ LONG-OK ]
+ ecCalibrate(&state->ec, CAL_RESET | CAL_CENTRE); // START software calibration
+ state->hold = 0;
+ state->calib |= CAL_RANGE;
+ state->flash = 8; // start with flash ON
+ used = true;
+ break;
+
+ default: break ;
+ }
+ break;
+
+ default: break ;
+ }
+
+ return used;
+}
+
+//+============================================================================ ========================================
+// WAIT screen
+//
+static inline
+bool wait_key (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ if (msg->input.type == InputTypeShort) {
+ switch (msg->input.key) {
+ case InputKeyLeft: //# run = false;
+ used = true;
+ break;
+
+ default: break ;
+ }
+ }
+
+ return used;
+}
+
+//+============================================================================ ========================================
+// DEBUG screen
+//
+static inline
+bool debug_key (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ switch (msg->input.type) {
+ case InputTypeShort: //# input.key) {
+ case InputKeyUp: { //# ec, NULL); // Initialise the controller //! NULL = no encryption
+ (void)init; // in case INFO is optimised out
+ INFO("%s : %s", __func__, (init ? "init OK" : "init fail"));
+ used = true;
+ break;
+ }
+
+ case InputKeyOk: //# ec) == 0) { // Read the controller
+ INFO( "%s : joy: {%02X,%02X,%02X,%02X,%02X,%02X}", __func__,
+ state->ec.joy[0], state->ec.joy[1], state->ec.joy[2],
+ state->ec.joy[3], state->ec.joy[4], state->ec.joy[5] );
+ }
+ used = true;
+ break;
+
+ case InputKeyDown: //# scenePrev);
+ used = true;
+ break;
+
+ case InputKeyBack: //# run = false;
+ used = true;
+ break;
+
+ default: break ; //#
+ }
+ break;
+
+ default: break ;
+ }
+
+ return used;
+}
+
+//+============================================================================ ========================================
+// SPLASH and RIP screen
+//
+static inline
+bool splash_key (const eventMsg_t* const msg, state_t* const state)
+{
+ // Back key on the initial SPLASH screen (this will catch the InputKeyPress)
+ if ((msg->input.key == InputKeyBack) && (state->scenePrev == SCENE_NONE)) state->run = false ;
+
+ // ANY-other-KEY press
+ if (msg->input.type == InputTypeShort) {
+ timerEn(state, true); // Restart the timer
+ state->scene = state->scenePegg;
+ }
+
+ return true;
+}
+
+
+//+============================================================================ ========================================
+// "_pre" allows the plugin to use the key before the active scene gets a chance
+//
+static inline
+bool key_pre (const eventMsg_t* const msg, state_t* const state)
+{
+ (void)msg;
+ (void)state;
+
+ return false;
+}
+
+//+============================================================================ ========================================
+// "_post" allows the plugin to use a key if it was not used by the active scene
+//
+static inline
+bool key_post (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ if (msg->input.key == InputKeyBack) {
+ if (msg->input.type == InputTypeShort) { //# ec.init = false; // reset/disconnect the controller
+ sceneSet(state, SCENE_WAIT);
+ used = true;
+
+ } else if (msg->input.type == InputTypeLong) { //# >B [LONG-BACK]
+ state->run = false; // Signal the plugin to exit
+ used = true;
+ }
+ }
+
+ // Easter eggs
+ switch (state->scene) {
+ case SCENE_SPLASH: // Scenes that do NOT offer Easter eggs
+ case SCENE_RIP:
+ case SCENE_DEBUG:
+ break;
+ default:
+ if (msg->input.type == InputTypeLong) {
+ switch (msg->input.key) {
+ case InputKeyDown: //# >D [LONG-DOWN]
+ timerEn(state, false); // Stop the timer
+ sceneSetEgg(state, SCENE_DEBUG);
+ used = true;
+ break;
+
+ case InputKeyLeft: //# >L [ LONG-LEFT ]
+ timerEn(state, false); // Stop the timer
+ sceneSetEgg(state, SCENE_SPLASH);
+ used = true;
+ break;
+
+ case InputKeyUp: //# >U [ LONG-UP ]
+ timerEn(state, false); // Stop the timer
+ sceneSetEgg(state, SCENE_RIP);
+ used = true;
+ break;
+
+ default: break ;
+ }
+ }
+ break;
+ }
+
+ return used;
+}
+
+//+============================================================================ ========================================
+// Handle a key press event
+//
+bool evKey (const eventMsg_t* const msg, state_t* const state)
+{
+ furi_assert(msg);
+ furi_assert(state);
+
+ bool used = key_pre(msg, state);
+
+ if (!used) switch (state->scene) {
+ case SCENE_SPLASH: //...
+ case SCENE_RIP: used = splash_key(msg, state); break ;
+
+ case SCENE_WAIT: used = wait_key(msg, state); break ;
+ case SCENE_DEBUG: used = debug_key(msg, state); break ;
+
+ default:
+ if (state->ec.pidx >= PID_ERROR) {
+ ERROR("%s : bad PID = %d", __func__, state->ec.pidx);
+ } else {
+ if ((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].keys)
+ ecId[PID_UNKNOWN].keys(msg, state);
+ else
+ ecId[state->ec.pidx].keys(msg, state);
+ }
+ break;
+
+ case SCENE_NONE: break;
+ }
+
+ if (!used) used = key_post(msg, state) ;
+
+ return used;
+}
+
diff --git a/applications/plugins/wii_ec_anal/wii_anal_keys.h b/applications/plugins/wii_ec_anal/wii_anal_keys.h
new file mode 100644
index 000000000..0ebbd5e8e
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_keys.h
@@ -0,0 +1,16 @@
+#ifndef WII_ANAL_KEYS_H_
+#define WII_ANAL_KEYS_H_
+
+//============================================================================= ========================================
+// Function prototypes
+//
+#include // bool
+typedef struct eventMsg eventMsg_t ;
+typedef struct state state_t ;
+typedef enum scene scene_t ;
+
+void sceneSet (state_t* const state, const scene_t scene) ;
+bool key_calib (const eventMsg_t* const msg, state_t* const state) ;
+bool evKey (const eventMsg_t* const msg, state_t* const state) ;
+
+#endif //WII_ANAL_KEYS_H_
diff --git a/applications/plugins/wii_ec_anal/wii_anal_lcd.c b/applications/plugins/wii_ec_anal/wii_anal_lcd.c
new file mode 100644
index 000000000..d031bc120
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_lcd.c
@@ -0,0 +1,223 @@
+#include "wii_anal.h"
+#include "gfx/images.h" // Images
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// A couple of monospaced hex fonts
+//
+const image_t* img_6x8[16] = {
+ &img_6x8_0, &img_6x8_1, &img_6x8_2, &img_6x8_3, &img_6x8_4, &img_6x8_5, &img_6x8_6, &img_6x8_7,
+ &img_6x8_8, &img_6x8_9, &img_6x8_A, &img_6x8_B, &img_6x8_C, &img_6x8_D, &img_6x8_E, &img_6x8_F,
+};
+
+const image_t* img_5x7[16] = {
+ &img_5x7_0, &img_5x7_1, &img_5x7_2, &img_5x7_3, &img_5x7_4, &img_5x7_5, &img_5x7_6, &img_5x7_7,
+ &img_5x7_8, &img_5x7_9, &img_5x7_A, &img_5x7_B, &img_5x7_C, &img_5x7_D, &img_5x7_E, &img_5x7_F,
+};
+
+//+============================================================================ ========================================
+// void backlightOn (void)
+// {
+// // Acquire a handle for the system notification queue
+// // Do this ONCE ... at plugin startup
+// NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
+//
+// // Pat the backlight watchdog
+// // Send the (predefined) message sequence {backlight_on, end}
+// // --> applications/notification/*.c
+// notification_message(notifications, &sequence_display_backlight_on);
+//
+// // Release the handle for the system notification queue
+// // Do this ONCE ... at plugin quit
+// furi_record_close(RECORD_NOTIFICATION);
+// }
+void patBacklight (state_t* state)
+{
+ notification_message(state->notify, &sequence_display_backlight_on);
+}
+
+//============================================================================= ========================================
+// Show a hex number in an inverted box (for ananlogue readings)
+//
+void showHex ( Canvas* const canvas, uint8_t x, uint8_t y,
+ const uint32_t val, const uint8_t cnt, const int b )
+{
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_box(canvas, x++,y++, 1 +(cnt *(6 +1)), 10);
+
+ // thicken border
+ if (b == 2) canvas_draw_frame(canvas, x-2,y-2, 1 +(cnt *(6 +1))+2, 10+2);
+
+ for (int i = (cnt -1) *4; i >= 0; i -= 4, x += 6+1)
+ show(canvas, x,y, img_6x8[(val >>i) &0xF], SHOW_SET_WHT) ;
+}
+
+//============================================================================= ========================================
+// Show the up/down "peak hold" controls in the bottom right
+//
+void showPeakHold (state_t* const state, Canvas* const canvas, const int hold)
+{
+ switch (hold) {
+ case 0:
+ show(canvas, 119,51, &img_key_U, SHOW_CLR_BLK);
+ show(canvas, 119,56, &img_key_D, SHOW_CLR_BLK);
+ break;
+ case +1:
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_box(canvas, 120,52, 7,6);
+ show(canvas, 119,51, &img_key_U, SHOW_CLR_WHT);
+ show(canvas, 119,56, &img_key_D, SHOW_CLR_BLK);
+ break;
+ case -1:
+ show(canvas, 119,51, &img_key_U, SHOW_CLR_BLK);
+ canvas_draw_box(canvas, 120,57, 7,6);
+ show(canvas, 119,56, &img_key_D, SHOW_CLR_WHT);
+ break;
+ default:
+ break;
+ }
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_frame(canvas, 119,51, 9,13);
+
+ // calibration indicator
+ show( canvas, 108,55,
+ ((state->calib & CAL_RANGE) && (++state->flash &8)) ? &img_key_OKi : &img_key_OK,
+ SHOW_SET_BLK );
+}
+
+//============================================================================= ========================================
+// This code performs a FULL calibration on the device EVERY time it draws a joystick
+//...This is NOT a good way forward for anything other than a test tool.
+//
+// Realistically you would do all the maths when the controller is connected
+// or, if you prefer (and it IS a good thing), have a "calibrate controller" menu option
+// ...and then just use a lookup table, or trivial formual
+//
+// THIS algorithm chops the joystick in to one of 9 zones
+// Eg. {FullLeft, Left3, Left2, Left1, Middle, Right1, Right2, Right3, FullRight}
+// FullLeft and FullRight have a deadzone of N [qv. xDead] ..a total of N+1 positions
+// Middle has a deadzone of N EACH WAY ...a total of 2N+1 positions
+//
+// If the remaining range does not divide evenly in to three zones,
+// the first remainder is added to zone3,
+// and the second remainder (if there is one) is added to zone2
+// ...giving finer control near the centre of the joystick
+//
+// The value of the deadzone is based on the number of bits in the
+// joystcik {x,y} values - the larger the range, the larger the deadzone.
+//
+// 03 15 29
+// |<<| Calibration points |==| |>>|
+// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+// |---| |________________________| |------| |______________________________| |---|
+// |r=2| | range = 9 | | r=3 | | range = 11 | |r=2|
+// Zones: |-4 | |-3 |-2 |-1 | |0 | |+1 |+2 |+3 | |+4 |
+//
+// This is not "the right way to do it" ...this is "one way to do it"
+// Consider you application, and what the user is trying to achieve
+// Aim a gun - probably need to be more accurate
+// Turn and object - this is probably good enough
+// Start slowly & pick up speed - how about a log or sine curve?
+//
+void showJoy ( Canvas* const canvas, const uint8_t x, const uint8_t y, // x,y is the CENTRE of the Joystick
+ const uint8_t xMin, const uint8_t xMid, const uint8_t xMax,
+ const uint8_t yMin, const uint8_t yMid, const uint8_t yMax,
+ const uint8_t xPos, const uint8_t yPos, const uint8_t bits )
+{
+ int xOff = 0; // final offset of joystick hat image
+ int yOff = 0;
+
+ int xDead = (bits < 7) ? (1<<0) : (1<<3); // dead zone (centre & limits)
+ int yDead = xDead;
+
+ // This code is NOT optimised ...and it's still barely readable!
+ if ((xPos >= (xMid -xDead)) && (xPos <= (xMid +xDead))) xOff = 0 ; // centre [most likely]
+ else if (xPos <= (xMin +xDead)) xOff = -4 ; // full left
+ else if (xPos >= (xMax -xDead)) xOff = +4 ; // full right
+ else if (xPos < (xMid -xDead)) { // part left
+ // very much hard-coded for 3 interim positions
+ int lo = (xMin +xDead) +1; // lowest position
+ int hi = (xMid -xDead) -1; // highest position
+
+ // this is the only duplicated bit of code
+ int range = (hi -lo) +1; // range covered
+ int div = range /3; // each division (base amount, eg. 17/3==5)
+ int rem = range -(div *3); // remainder (ie. range%3)
+
+// int hi1 = hi; // lowest value for zone #-1
+// int lo1 = hi1 -div +1; // highest value for zone #-1
+// int hi2 = lo1 -1; // lowest value for zone #-2
+// int lo2 = hi2 -div +1 -(rem==2); // highest value for zone #-2 expand out remainder
+// int hi3 = lo2 -1; // lowest value for zone #-3
+// int lo3 = hi3 -div +1 -(rem>=1); // highest value for zone #-3 expand out remainder
+
+ int lo1 = hi -div +1; // (in brevity)
+ int hi3 = hi -div -div -(rem==2); // ...
+
+ if (xPos <= hi3) xOff = -3 ; // zone #-3
+ else if (xPos >= lo1) xOff = -1 ; // zone #-1
+ else xOff = -2 ; // zone #-2
+
+ } else /*if (xPos > (xMid +xDead))*/ { // part right
+ // very much hard-coded for 3 interim positions
+ int lo = (xMid +xDead) +1; // lowest position
+ int hi = (xMax -xDead) -1; // highest position
+
+ int range = (hi -lo) +1; // range covered
+ int div = range /3; // each division (base amount, eg. 17/3==5)
+ int rem = range -(div *3); // remainder (ie. range%3)
+
+// int lo1 = lo; // lowest value for zone #+1
+// int hi1 = lo +div -1; // highest value for zone #+1
+// int lo2 = hi1 +1; // lowest value for zone #+2
+// int hi2 = lo2 +div -1 +(rem==2); // highest value for zone #+2 expand out remainder
+// int lo3 = hi2 +1; // lowest value for zone #+3
+// int hi3 = lo3 +div -1 +(rem>=1); // highest value for zone #+3 expand out remainder
+
+ int hi1 = lo +div -1; // (in brevity)
+ int lo3 = lo +div +div +(rem==2); // ...
+
+ if (xPos <= hi1) xOff = 1 ; // zone #1
+ else if (xPos >= lo3) xOff = 3 ; // zone #3
+ else xOff = 2 ; // zone #2
+ }
+
+ // All this to print a 3x3 square (in the right place) - LOL!
+ if ((yPos >= (yMid -yDead)) && (yPos <= (yMid +yDead))) yOff = 0 ; // centre [most likely]
+ else if (yPos <= (yMin +yDead)) yOff = +4 ; // full down
+ else if (yPos >= (yMax -yDead)) yOff = -4 ; // full up
+ else if (yPos < (yMid -yDead)) { // part down
+ int lo = (yMin +yDead) +1; // lowest position
+ int hi = (yMid -yDead) -1; // highest position
+
+ int range = (hi -lo) +1; // range covered
+ int div = range /3; // each division (base amount, eg. 17/3==5)
+ int rem = range -(div *3); // remainder (ie. range%3)
+
+ int lo1 = hi -div +1; // (in brevity)
+ int hi3 = hi -div -div -(rem==2); // ...
+
+ if (yPos <= hi3) yOff = +3 ; // zone #3
+ else if (yPos >= lo1) yOff = +1 ; // zone #1
+ else yOff = +2 ; // zone #2
+
+ } else /*if (yPos > (yMid +yDead))*/ { // part up
+ int lo = (yMid +yDead) +1; // lowest position
+ int hi = (yMax -yDead) -1; // highest position
+
+ int range = (hi -lo) +1; // range covered
+ int div = range /3; // each division (base amount, eg. 17/3==5)
+ int rem = range -(div *3); // remainder (ie. range%3)
+
+ int hi1 = lo +div -1; // (in brevity)
+ int lo3 = lo +div +div +(rem==2); // ...
+
+ if (yPos <= hi1) yOff = -1 ; // zone #-1
+ else if (yPos >= lo3) yOff = -3 ; // zone #-3
+ else yOff = -2 ; // zone #-2
+ }
+
+ show(canvas, x-(img_cc_Joy.w/2),y-(img_cc_Joy.h/2), &img_cc_Joy, SHOW_SET_BLK);
+
+ // All ^that^ for v-this-v - LOL!!
+ canvas_draw_box(canvas, (x-1)+xOff,(y-1)+yOff, 3,3);
+}
diff --git a/applications/plugins/wii_ec_anal/wii_anal_lcd.h b/applications/plugins/wii_ec_anal/wii_anal_lcd.h
new file mode 100644
index 000000000..5258c4de1
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_lcd.h
@@ -0,0 +1,43 @@
+#ifndef WII_ANAL_LCD_H_
+#define WII_ANAL_LCD_H_
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// A couple of monospaced hex fonts
+//
+#include "gfx/images.h"
+
+extern const image_t* img_6x8[];
+extern const image_t* img_5x7[];
+
+//============================================================================= ========================================
+// macros to draw only two sides of a box
+// these are used for drawing the wires on the WAIT screen
+//
+#define BOX_TL(x1,y1,x2,y2) do { \
+ canvas_draw_frame(canvas, x1,y1, x2-x1+1,2); \
+ canvas_draw_frame(canvas, x1,y1+2, 2,y2-y1+1-2); \
+}while(0)
+
+#define BOX_BL(x1,y1,x2,y2) do { \
+ canvas_draw_frame(canvas, x1,y2-1, x2-x1+1,2); \
+ canvas_draw_frame(canvas, x1,y1, 2,y2-y1+1-2); \
+}while(0)
+
+
+//============================================================================= ========================================
+// Function prototypes
+//
+void patBacklight (state_t* state) ;
+
+void showHex ( Canvas* const canvas, uint8_t x, uint8_t y,
+ const uint32_t val, const uint8_t cnt, const int b ) ;
+
+void showPeakHold (state_t* const state, Canvas* const canvas, const int hold) ;
+
+void showJoy ( Canvas* const canvas, const uint8_t x, const uint8_t y, // x,y is the CENTRE of the Joystick
+ const uint8_t xMin, const uint8_t xMid, const uint8_t xMax,
+ const uint8_t yMin, const uint8_t yMid, const uint8_t yMax,
+ const uint8_t xPos, const uint8_t yPos, const uint8_t bits ) ;
+
+
+#endif //WII_ANAL_LCD_H_
diff --git a/applications/plugins/wii_ec_anal/wii_anal_ver.h b/applications/plugins/wii_ec_anal/wii_anal_ver.h
new file mode 100644
index 000000000..88ed17d19
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_anal_ver.h
@@ -0,0 +1,9 @@
+#ifndef WII_ANAL_VER_H_
+#define WII_ANAL_VER_H_
+
+#include "gfx/images.h"
+
+#define VER_MAJ &img_3x5_1
+#define VER_MIN &img_3x5_0
+
+#endif //WII_ANAL_VER_H_
diff --git a/applications/plugins/wii_ec_anal/wii_ec.c b/applications/plugins/wii_ec_anal/wii_ec.c
new file mode 100644
index 000000000..87b4edcfb
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec.c
@@ -0,0 +1,214 @@
+#include
+#include // Core API
+
+#include "wii_anal.h"
+#include "wii_i2c.h"
+#include "wii_ec.h"
+#include "bc_logging.h"
+
+#include "gfx/images.h" // Images
+#include "wii_anal_lcd.h" // Drawing functions
+#include "wii_anal_keys.h" // key mappings
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// List of known perhipherals
+//
+// More perhipheral ID codes here: https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way
+//
+const ecId_t ecId[PID_CNT] = {
+ [PID_UNKNOWN ] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Unknown Perhipheral", SCENE_DUMP,
+ NULL, NULL, NULL, NULL, ec_show, ec_key},
+
+ // If you're wise, ONLY edit this bit
+ [PID_NUNCHUCK ] = { {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, "Nunchuck", SCENE_NUNCHUCK,
+ NULL, nunchuck_decode, nunchuck_msg, nunchuck_calib, nunchuck_show, nunchuck_key },
+
+ [PID_CLASSIC ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x01}, "Classic Controller", SCENE_CLASSIC,
+ NULL, classic_decode, classic_msg, classic_calib, classic_show, classic_key },
+
+ [PID_BALANCE ] = { {0x00, 0x00, 0xA4, 0x20, 0x04, 0x02}, "Balance Board", SCENE_DUMP,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ [PID_GH_GUITAR ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero Guitar", SCENE_DUMP,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ [PID_GH_DRUMS ] = { {0x01, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero World Tour Drums", SCENE_DUMP,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ [PID_TURNTABLE ] = { {0x03, 0x00, 0xA4, 0x20, 0x01, 0x03}, "DJ Hero Turntable", SCENE_DUMP,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ [PID_TAIKO_DRUMS] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x11}, "Taiko Drum Controller)", SCENE_DUMP,
+ NULL, NULL, NULL, NULL, NULL, NULL }, // Taiko no Tatsujin TaTaCon (Drum controller)
+
+ [PID_UDRAW ] = { {0xFF, 0x00, 0xA4, 0x20, 0x00, 0x13}, "uDraw Tablet", SCENE_DUMP,
+ udraw_init, NULL, NULL, NULL, NULL, NULL }, //! same as drawsome?
+ // -----
+
+ [PID_ERROR ] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, "Read Error", SCENE_NONE,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ [PID_NULL ] = { {0}, NULL, SCENE_NONE, NULL, NULL, NULL, NULL, NULL, NULL } // last entry
+};
+
+//+============================================================================ ========================================
+void ecDecode (wiiEC_t* pec)
+{
+ if (ecId[pec->pidx].decode) ecId[pec->pidx].decode(pec) ;
+}
+
+//+============================================================================ ========================================
+void ecCalibrate (wiiEC_t* const pec, ecCalib_t c)
+{
+ if (ecId[pec->pidx].calib) ecId[pec->pidx].calib(pec, c) ;
+}
+
+//+============================================================================ ========================================
+void ecPoll (wiiEC_t* const pec, FuriMessageQueue* const queue)
+{
+ ENTER;
+ furi_assert(queue);
+
+ if (!pec->init) {
+ // Attempt to initialise
+ if (ecInit(pec, NULL)) { //! need a way to auto-start with encryption enabled
+ eventMsg_t msg = {
+ .id = EVID_WIIEC,
+ .wiiEc = {
+ .type = WIIEC_CONN,
+ .in = '<',
+ .val = pec->pidx
+ }
+ };
+ furi_message_queue_put(queue, &msg, 0);
+ }
+
+ } else {
+ // Attempt to read
+ switch (ecRead(pec)) {
+ case 2: { // device gone
+ eventMsg_t msg = {
+ .id = EVID_WIIEC,
+ .wiiEc = {
+ .type = WIIEC_DISCONN,
+ .in = '>',
+ .val = pec->pidx
+ }
+ };
+ furi_message_queue_put(queue, &msg, 0);
+ break;
+ }
+
+ case 0: { // read OK
+ void (*fn)(wiiEC_t*, FuriMessageQueue*) = ecId[pec->pidx].check;
+ if (fn) fn(pec, queue);
+ break;
+ }
+
+ case 3: // read fail
+ // this is probably temporary just ignore it
+ break;
+
+ default: // bug: unknown
+ case 1: // bug: not initialised - should never happen
+ ERROR("%s : read bug", __func__);
+ break;
+ }
+ }
+
+ LEAVE;
+ return;
+}
+
+//+============================================================================ ========================================
+// This is the screen drawn for an unknown controller
+// It is also available by pressing LEFT (at least once) on a "known controller" screen
+//
+void ec_show (Canvas* const canvas, state_t* const state)
+{
+ wiiEC_t* pec = &state->ec;
+ int h = 11; // line height
+ int x = 1; // (initial) offset for bits
+ int y = -h; // previous y value
+ int yb = 0; // y for bit patterns
+ int c2 = 17; // column 2
+
+ // Headings
+ canvas_set_font(canvas, FontSecondary);
+ canvas_set_color(canvas, ColorBlack);
+
+ canvas_draw_str_aligned(canvas, 0 ,0, AlignLeft, AlignTop, "SID:");
+ canvas_draw_str_aligned(canvas, c2,0, AlignLeft, AlignTop, pec->sid);
+
+ canvas_draw_str_aligned(canvas, 0 ,11, AlignLeft, AlignTop, "PID:");
+ canvas_draw_str_aligned(canvas, 0 ,22, AlignLeft, AlignTop, "Cal:");
+
+ // PID
+ x = c2;
+ for (int i = 0; i < 6; i++) {
+ show(canvas, x,11, img_5x7[pec->pid[i]>>4], SHOW_SET_BLK);
+ x += 5+1;
+ show(canvas, x,11, img_5x7[pec->pid[i]&0xF], SHOW_SET_BLK);
+ x += 5+1+2;
+ }
+
+ // Calibrations data
+ y = 11;
+ for (int j = 0; j <= 8; j += 8) {
+ x = c2;
+ y += 11;
+ for (int i = 0; i < 8; i++) {
+ show(canvas, x,y, img_5x7[pec->calF[i+j]>>4], SHOW_SET_BLK);
+ x += 5+1;
+ show(canvas, x,y, img_5x7[pec->calF[i+j]&0xF], SHOW_SET_BLK);
+ x += 5+1+2;
+ }
+ }
+
+ // Reading
+ x = 1;
+ y++;
+ yb = (y+=h) +h +2;
+
+ canvas_draw_line(canvas, x,y-1, x,yb+4);
+ x += 2;
+
+ for (int i = 0; i < JOY_LEN; i++) {
+ show(canvas, x+ 1,y, img_6x8[pec->joy[i]>>4], SHOW_SET_BLK);
+ show(canvas, x+11,y, img_6x8[pec->joy[i]&0xF], SHOW_SET_BLK);
+
+ // bits
+ for (int m = 0x80; m; m >>= 1) {
+ x += 2 * !!(m & 0x08) ; // nybble step
+ canvas_draw_box(canvas, x,yb +(2*!(pec->joy[i] & m)), 2,2) ;
+ x += 2; // bit step
+ }
+
+ // byte step
+ x += 1;
+ canvas_draw_line(canvas, x,y-1, x,yb+4);
+ x += 2;
+ }
+
+ // Scene navigation
+ if (state->scenePrev != SCENE_WAIT)
+ show(canvas, 120,0, &img_key_R, SHOW_SET_BLK);
+}
+
+//+============================================================================ ========================================
+// The DUMP screen is
+//
+bool ec_key (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ if (state->scenePrev != SCENE_WAIT) {
+ //# input.type == InputTypeShort) && (msg->input.key == InputKeyRight)) {
+ sceneSet(state, state->scenePrev);
+ used = true;
+ }
+ }
+
+ return used;
+}
diff --git a/applications/plugins/wii_ec_anal/wii_ec.h b/applications/plugins/wii_ec_anal/wii_ec.h
new file mode 100644
index 000000000..60f7c7b23
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec.h
@@ -0,0 +1,175 @@
+#ifndef WII_EC_H_
+#define WII_EC_H_
+
+#include
+
+#include
+
+#include "wii_ec_nunchuck.h"
+#include "wii_ec_classic.h"
+#include "wii_ec_udraw.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Crypto key (PSK), base register : {0x40..0x4F}[2][8]
+#define ENC_LEN (2*8)
+
+// Controller State data, base register : {0x00..0x05}[6]
+#define JOY_LEN (6)
+
+// Calibration data, base register : {0x20..0x2F}[16]
+#define CAL_LEN (16)
+
+// Controller ID, base register : {0xFA..0xFF}[6]
+#define PID_LEN (6)
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Perhipheral specific parameters union
+//
+typedef
+ union ecDec {
+ ecDecNunchuck_t nunchuck;
+ ecDecClassic_t classic;
+ }
+ecDec_t;
+
+//-----------------------------------------------------------------------------
+typedef
+ union ecCal {
+ // 0=lowest seen ; 1=min ; 2=mid ; 3=max ; 4=highest seen
+ ecCalNunchuck_t nunchuck[5];
+ ecCalClassic_t classic[5];
+ }
+ecCal_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Wii Extension Controller events
+//
+typedef
+ enum wiiEcEventType {
+ WIIEC_NONE,
+ WIIEC_CONN, // Connect
+ WIIEC_DISCONN, // Disconnect
+ WIIEC_PRESS, // Press button
+ WIIEC_RELEASE, // Release button
+ WIIEC_ANALOG, // Analogue change (Joystick/Trigger)
+ WIIEC_ACCEL, // Accelerometer change
+ }
+wiiEcEventType_t;
+
+//-----------------------------------------------------------------------------
+typedef
+ struct wiiEcEvent {
+ wiiEcEventType_t type; // event type
+ char in; // input (see device specific options)
+ uint32_t val; // new value - meaningless for digital button presses
+ }
+wiiEcEvent_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Known perhipheral types
+//
+typedef
+ enum ecPid {
+ PID_UNKNOWN = 0,
+ PID_FIRST = 1,
+ PID_NUNCHUCK = PID_FIRST,
+
+ // If you're wise, ONLY edit this section
+ PID_CLASSIC,
+ PID_BALANCE,
+ PID_GH_GUITAR,
+ PID_GH_DRUMS,
+ PID_TURNTABLE,
+ PID_TAIKO_DRUMS,
+ PID_UDRAW, //! same as drawsome?
+ // -----
+
+ PID_ERROR,
+ PID_NULL,
+ PID_CNT,
+ }
+ecPid_t;
+
+//-----------------------------------------------------------------------------
+// Calibration strategies
+//
+typedef
+ enum ecCalib {
+ CAL_FACTORY = 0x01, // (re)set to factory defaults
+ CAL_TRACK = 0x02, // track maximum and minimum values seen
+ CAL_RESET = 0x04, // initialise ready for software calibration
+ CAL_RANGE = 0x08, // perform software calibration step
+ CAL_CENTRE = 0x10, // reset centre point of joystick
+ CAL_NOTJOY = 0x20, // do NOT calibrate the joystick
+ }
+ecCalib_t;
+
+//-----------------------------------------------------------------------------
+// ecId table entry
+//
+typedef
+ struct ecId {
+ uint8_t id[6]; // 6 byte ID string returned by Extension Controller
+ char* name; // Friendly name
+ scene_t scene; // Default scene
+ bool (*init)(wiiEC_t*); // Additional initialisation code
+ void (*decode)(wiiEC_t*); // Decode function
+ void (*check)(wiiEC_t*, FuriMessageQueue*); // check (for action) function
+ void (*calib)(wiiEC_t*, ecCalib_t); // calibrate analogue controllers [SOFTWARE]
+ void (*show)(Canvas* const, state_t* const); // Draw scene
+ bool (*keys)(const eventMsg_t* const, state_t* const); // Interpret keys
+ }
+ecId_t;
+
+//-----------------------------------------------------------------------------
+// List of known perhipherals
+//
+// More perhipheral ID codes here: https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way
+//
+extern const ecId_t ecId[PID_CNT] ;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Data pertaining to a single Perhipheral instance
+//
+typedef
+ struct wiiEC {
+ // Perhipheral state
+ bool init; // Initialised?
+
+ uint8_t pid[PID_LEN]; // PID string - eg. {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}
+ ecPid_t pidx; // Index in to ecId table
+ const char* sid; // just for convenience
+
+ bool encrypt; // encryption enabled?
+ uint8_t encKey[ENC_LEN]; // encryption key
+
+ uint8_t calF[CAL_LEN]; // factory calibration data (not software)
+ uint8_t joy[JOY_LEN]; // Perhipheral raw data
+
+ ecDec_t dec[2]; // device specific decode (two, so we can spot changes)
+ int decN; // which decode set is most recent {0, 1}
+ ecCal_t calS; // software calibration data
+ }
+wiiEC_t;
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Function prototypes
+//
+// top level calls will work out which sub-function to call
+// top level check() function will handle connect/disconnect messages
+//
+
+#include // Canvas
+typedef struct wiiEC wiiEC_t ;
+typedef enum ecCalib ecCalib_t ;
+typedef struct state state_t ;
+typedef struct eventMsg eventMsg_t ;
+
+void ecDecode (wiiEC_t* const pec) ;
+void ecPoll (wiiEC_t* const pec, FuriMessageQueue* const queue) ;
+void ecCalibrate (wiiEC_t* const pec, ecCalib_t c) ;
+
+void ec_show ( Canvas* const canvas, state_t* const state) ;
+bool ec_key (const eventMsg_t* const msg, state_t* const state) ;
+
+#endif //WII_EC_H_
diff --git a/applications/plugins/wii_ec_anal/wii_ec_classic.c b/applications/plugins/wii_ec_anal/wii_ec_classic.c
new file mode 100644
index 000000000..91393ba07
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec_classic.c
@@ -0,0 +1,398 @@
+#include
+#include // Core API
+
+#include "wii_anal.h"
+#include "wii_ec.h"
+#include "bc_logging.h"
+
+//#include "gfx/images.h" // Images
+#include "wii_anal_lcd.h" // Drawing functions
+#include "wii_anal_keys.h" // key mappings
+
+// ** If you want to see what this source code looks like with all the MACROs expanded
+// ** grep -v '#include ' wii_i2c_classic.c | gcc -E -o /dev/stdout -xc -
+# include "wii_ec_macros.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Classic Controller ... Classic Controller Pro is electronically the same
+//
+// ANA{l} ANA{r}
+// BTN{l} BTN{L} BTN{R} BTN{r}
+// ,--------. ,-, ,-, .--------,
+// .----------------------------------------------------------.
+// | |
+// | BTN{W} BTN{x} |
+// | BTN{A} BTN{D} BTN{-} BTN{h} BTN{+} BTN{y} BTN{a} |
+// | BTN{S} BTN{b} |
+// | |
+// | ANA{y} ANA{Y} |
+// | ANA{x} ANA{X} |
+// | |
+// `----------------------------------------------------------'
+
+//+============================================================================ ========================================
+// https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller
+// I think a LOT of drugs went in to "designing" this layout
+// ...And yes, the left-joystick has an extra 'bit' of precision!
+// ...Also: trgZ{L|R} WILL continue to increase after btnZ{L|R} has gone active
+//
+void classic_decode (wiiEC_t* const pec)
+{
+ ecDecClassic_t* p = &pec->dec[(pec->decN = !pec->decN)].classic;
+ uint8_t* joy = pec->joy;
+
+ p->trgZL = ((joy[2] >>2) &0x18) | ((joy[3] >>5) &0x07); // {5}
+ p->btnZL = !(joy[4] & 0x20); // !{1}
+
+ p->trgZR = joy[3] & 0x1F; // {5}
+ p->btnZR = !(joy[4] & 0x02); // !{1}
+
+ p->btnL = !(joy[5] & 0x80); // !{1}
+ p->btnR = !(joy[5] & 0x04); // !{1}
+
+ p->padU = !(joy[5] & 0x01); // !{1}
+ p->padD = !(joy[4] & 0x40); // !{1}
+ p->padL = !(joy[5] & 0x02); // !{1}
+ p->padR = !(joy[4] & 0x80); // !{1}
+
+ p->btnM = !(joy[4] & 0x10); // !{1}
+ p->btnH = !(joy[4] & 0x08); // !{1}
+ p->btnP = !(joy[4] & 0x04); // !{1}
+
+ p->btnX = !(joy[5] & 0x08); // !{1}
+ p->btnY = !(joy[5] & 0x20); // !{1}
+
+ p->btnA = !(joy[5] & 0x10); // !{1}
+ p->btnB = !(joy[5] & 0x40); // !{1}
+
+ p->joyLX = joy[0] & 0x3F; // {6}
+ p->joyLY = joy[1] & 0x3F; // {6}
+
+ p->joyRX = ((joy[0] >>3) &0x18) | ((joy[1] >>5) &0x06) | ((joy[2] >>7) &0x01); // {5}
+ p->joyRY = joy[2] & 0x1F; // {5}
+
+ DEBUG( ">%d> ZL{%02X}%c, L:%c, R:%c, ZR{%02X}%c", pec->decN,
+ p->trgZL, (p->btnZL ? '#' : '.'),
+ (p->btnL ? '#' : '.'),
+ (p->btnR ? '#' : '.'),
+ p->trgZR, (p->btnZR ? '#' : '.')
+ );
+ DEBUG( ">%d> D:{%c,%c,%c,%c}, H:{%c,%c,%c}, B:{%c,%c,%c,%c}", pec->decN,
+ (p->padU ? 'U' : '.'), (p->padD ? 'D' : '.'), (p->padL ? 'L' : '.'), (p->padR ? 'R' : '.'),
+ (p->btnM ? '-' : '.'), (p->btnH ? 'H' : '.'), (p->btnP ? '+' : '.'),
+ (p->btnX ? 'X' : '.'), (p->btnY ? 'Y' : '.'), (p->btnA ? 'A' : '.'), (p->btnB ? 'B' : '.')
+ );
+ DEBUG( ">%d> JoyL{x:%02X, y:%02X}, JoyR{x:%02X, y:%02X}", pec->decN,
+ p->joyLX, p->joyLY, p->joyRX, p->joyRY
+ );
+}
+
+//+============================================================================ ========================================
+// Give each button a unique character identifier
+//
+void classic_msg (wiiEC_t* const pec, FuriMessageQueue* const queue)
+{
+ ecDecClassic_t* new = &pec->dec[pec->decN].classic;
+ ecDecClassic_t* old = &pec->dec[!pec->decN].classic;
+
+ eventMsg_t msg = {
+ .id = EVID_WIIEC,
+ .wiiEc = {
+ .type = WIIEC_NONE,
+ .in = ' ',
+ .val = 0,
+ }
+ };
+
+ ANALOG(trgZL, 'l'); // FIVE bit value
+ ANABTN(btnZL, trgZL, 'l');
+
+ BUTTON(btnL, 'L');
+ BUTTON(btnR, 'R');
+
+ ANALOG(trgZR, 'r'); // FIVE bit value
+ ANABTN(btnZR, trgZR, 'r');
+
+ BUTTON(padU, 'W');
+ BUTTON(padL, 'A');
+ BUTTON(padD, 'S');
+ BUTTON(padR, 'D');
+
+ BUTTON(btnM, '-');
+ BUTTON(btnH, 'h');
+ BUTTON(btnP, '+');
+
+ BUTTON(btnX, 'x');
+ BUTTON(btnY, 'y');
+ BUTTON(btnA, 'a');
+ BUTTON(btnB, 'b');
+
+ ANALOG(joyLX, 'x'); // SIX bit values
+ ANALOG(joyLY, 'y');
+
+ ANALOG(joyRX, 'X'); // FIVE bit values
+ ANALOG(joyRY, 'Y');
+}
+
+//+============================================================================ ========================================
+// https://web.archive.org/web/20090415045219/http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Classic_Controller#Calibration_data
+//
+// Calibration data
+// 0..2 left analog stick X axis {maximum, minimum, center} ... JoyL is 6bits, so >>2 to compare to readings
+// 3..5 left analog stick Y axis {maximum, minimum, center} ... JoyL is 6bits, so >>2 to compare to readings
+// 6..8 right analog stick X axis {maximum, minimum, center} ... JoyR is 5bits, so >>3 to compare to readings
+// 9..11 right analog stick Y axis {maximum, minimum, center} ... JoyR is 5bits, so >>3 to compare to readings
+// 12..15 somehow describe the shoulder {5bit} button values!?
+//
+void classic_calib (wiiEC_t* const pec, ecCalib_t c)
+{
+ ecDecClassic_t* src = &pec->dec[pec->decN].classic; // from input
+ ecCalClassic_t* dst = pec->calS.classic; // to calibration data
+
+ if (c & CAL_RESET) { // initialise ready for software calibration
+ // LO is set to the MAXIMUM value (so it can be reduced)
+ // HI is set to ZERO (so it can be increased)
+ RESET_LO_HI(trgZL, 5); // 5bit value
+ RESET_LO_HI(trgZR, 5); // 5bit value
+
+ RESET_LO_MID_HI(joyLX, 6); // 6bit value
+ RESET_LO_MID_HI(joyLY, 6); // 6bit value
+
+ RESET_LO_MID_HI(joyRX, 5); // 5bit value
+ RESET_LO_MID_HI(joyRY, 5); // 5bit value
+ }
+ if (c & CAL_FACTORY) { // (re)set to factory defaults
+//! strategy for factory calibration for classic controller [pro] triggers is (currently) unknown
+//! FACTORY_LO( trgZL, pec->calF[12..15]);
+//! FACTORY_MID(trgZL, pec->calF[12..15]);
+//! FACTORY_HI( trgZL, pec->calF[12..15]);
+
+//! FACTORY_LO( trgZR, pec->calF[12..15]);
+//! FACTORY_MID(trgZR, pec->calF[12..15]);
+//! FACTORY_HI( trgZR, pec->calF[12..15]);
+
+#if 1
+ FACTORY_LO(trgZL, 0x03);
+ FACTORY_LO(trgZR, 0x03);
+
+ FACTORY_MID(trgZL, 0x1B); //! these will be set every time the digital switch changes to ON
+ FACTORY_MID(trgZR, 0x1B);
+#endif
+
+ FACTORY_LO( joyLX, pec->calF[ 1] >>2);
+ FACTORY_MID(joyLX, pec->calF[ 2] >>2);
+ FACTORY_HI( joyLX, pec->calF[ 0] >>2);
+
+ FACTORY_LO( joyLY, pec->calF[ 4] >>2);
+ FACTORY_MID(joyLY, pec->calF[ 5] >>2);
+ FACTORY_HI( joyLY, pec->calF[ 3] >>2);
+
+ FACTORY_LO( joyRX, pec->calF[ 7] >>3);
+ FACTORY_MID(joyRX, pec->calF[ 8] >>3);
+ FACTORY_HI( joyRX, pec->calF[ 6] >>3);
+
+ FACTORY_LO( joyRY, pec->calF[10] >>3);
+ FACTORY_MID(joyRY, pec->calF[11] >>3);
+ FACTORY_HI( joyRY, pec->calF[ 9] >>3);
+ }
+ if (c & CAL_TRACK) { // track maximum and minimum values seen
+ TRACK_LO_HI(trgZL);
+ TRACK_LO_HI(trgZR);
+
+ TRACK_LO_HI(joyLX);
+ TRACK_LO_HI(joyLY);
+
+ TRACK_LO_HI(joyRX);
+ TRACK_LO_HI(joyRY);
+ }
+ if (c & CAL_RANGE) { // perform software calibration step
+ RANGE_LO_HI(trgZL);
+ RANGE_LO_HI(trgZR);
+
+ RANGE_LO_HI(joyLX);
+ RANGE_LO_HI(joyLY);
+
+ RANGE_LO_HI(joyRX);
+ RANGE_LO_HI(joyRY);
+ }
+ if (c & CAL_CENTRE) { // reset centre point of joystick
+ CENTRE(joyLX);
+ CENTRE(joyLY);
+
+ CENTRE(joyRX);
+ CENTRE(joyRY);
+ }
+}
+
+//+============================================================================ ========================================
+// bits that are common to both screens
+//
+static
+void classic_show_ (Canvas* const canvas, state_t* const state)
+{
+ ecDecClassic_t* d = &state->ec.dec[state->ec.decN].classic;
+ ecCalClassic_t* js = state->ec.calS.classic;
+
+ static const int dead = 1; // trigger deadzone
+ const image_t* img = NULL; // trigger image
+
+ show(canvas, 6, 0, &img_cc_Main , SHOW_SET_BLK);
+ show(canvas, 62,53, &img_cc_Cable, SHOW_SET_BLK);
+
+ // classic triggers
+ if (d->trgZL >= js[2].trgZL ) img = &img_cc_trg_L4;
+ else if (d->trgZL <= js[1].trgZL +dead) img = NULL;
+ else {
+ // copied from the joystick calibration code
+ int lo = js[1].trgZL +dead +1;
+ int hi = js[2].trgZL -1;
+ int range = hi -lo +1;
+ int div = range /3; // each division (base amount, eg. 17/3==5)
+ int rem = range -(div *3); // remainder (ie. range%3)
+ int hi1 = lo +div -1; // (in brevity)
+ int lo3 = lo +div +div +(rem==2); // ...
+
+ if (d->trgZL <= hi1) img = &img_cc_trg_L1 ; // zone #1
+ else if (d->trgZL >= lo3) img = &img_cc_trg_L3 ; // zone #3
+ else img = &img_cc_trg_L2 ; // zone #2
+ }
+ if (img) show(canvas, 22,1, img, SHOW_SET_BLK) ;
+
+ if (d->trgZR >= js[2].trgZR ) img = &img_cc_trg_R4;
+ else if (d->trgZR <= js[1].trgZR +dead) img = NULL;
+ else {
+ // copied from the joystick calibration code
+ int lo = js[1].trgZR +dead +1;
+ int hi = js[2].trgZR -1;
+ int range = hi -lo +1;
+ int div = range /3; // each division (base amount, eg. 17/3==5)
+ int rem = range -(div *3); // remainder (ie. range%3)
+ int hi1 = lo +div -1; // (in brevity)
+ int lo3 = lo +div +div +(rem==2); // ...
+
+ if (d->trgZR <= hi1) img = &img_cc_trg_R1 ; // zone #1
+ else if (d->trgZR >= lo3) img = &img_cc_trg_R3 ; // zone #3
+ else img = &img_cc_trg_R2 ; // zone #2
+ }
+ if (img) show(canvas, 89,1, img, SHOW_SET_BLK) ;
+
+ if (d->padU ) show(canvas, 27,16, &img_cc_pad_UD1, SHOW_ALL) ;
+ if (d->padL ) show(canvas, 20,23, &img_cc_pad_LR1, SHOW_ALL) ;
+ if (d->padD ) show(canvas, 27,28, &img_cc_pad_UD1, SHOW_ALL) ;
+ if (d->padR ) show(canvas, 32,23, &img_cc_pad_LR1, SHOW_ALL) ;
+
+ if (d->btnX ) show(canvas, 96,16, &img_cc_btn_X1, SHOW_ALL) ;
+ if (d->btnY ) show(canvas, 85,23, &img_cc_btn_Y1, SHOW_ALL) ;
+ if (d->btnA ) show(canvas, 107,23, &img_cc_btn_A1, SHOW_ALL) ;
+ if (d->btnB ) show(canvas, 96,30, &img_cc_btn_B1, SHOW_ALL) ;
+
+ canvas_set_color(canvas, ColorBlack);
+ if (d->btnL ) canvas_draw_box(canvas, 46,2, 5,4) ;
+ if (d->btnR ) canvas_draw_box(canvas, 77,2, 5,4) ;
+
+ if (d->btnM ) canvas_draw_box(canvas, 54,24, 4,4) ;
+ if (d->btnH ) canvas_draw_box(canvas, 62,24, 4,4) ;
+ if (d->btnP ) canvas_draw_box(canvas, 70,24, 4,4) ;
+
+ // Show joysticks
+ showJoy(canvas, 48,42, js[1].joyLX,js[2].joyLX, js[3].joyLX,
+ js[1].joyLY,js[2].joyLY, js[3].joyLY, d->joyLX,d->joyLY, 6);
+ showJoy(canvas, 78,42, js[1].joyRX,js[2].joyRX, js[3].joyRX,
+ js[1].joyRY,js[2].joyRY, js[3].joyRY, d->joyRX,d->joyRY, 5);
+
+ show(canvas, 0,55, &img_key_L, SHOW_SET_BLK);
+}
+
+//+============================================================================ ========================================
+static
+void classic_showN (Canvas* const canvas, state_t* const state)
+{
+ ecCalClassic_t* c = (state->hold) ? &state->ec.calS.classic[(state->hold < 0) ? 0 : 4]
+ : (ecCalClassic_t*)(&state->ec.dec[state->ec.decN].classic) ; //! danger
+
+ classic_show_(canvas, state);
+
+ showHex(canvas, 0, 0, c->trgZL, 2,1); // 5bits
+ showHex(canvas, 113, 0, c->trgZR, 2,1); // 5bits
+
+ showHex(canvas, 24,41, c->joyLX, 2,1); // 6bits
+ showHex(canvas, 41,54, c->joyLY, 2,1); // 6bits
+
+ showHex(canvas, 88,41, c->joyRX, 2,1); // 5bits
+ showHex(canvas, 71,54, c->joyRY, 2,1); // 5bits
+
+ showPeakHold(state, canvas, state->hold); // peak keys
+}
+
+//+============================================================================ ========================================
+void classic_show (Canvas* const canvas, state_t* const state)
+{
+ // Classic controllers have TWO scenes
+ if (state->scene == SCENE_CLASSIC_N) return classic_showN(canvas, state) ;
+
+ // Default scene
+ classic_show_(canvas, state);
+ show(canvas, 9,55, &img_key_R, SHOW_SET_BLK);
+
+ show( canvas, 119,55,
+ ((state->calib & CAL_RANGE) && (++state->flash &8)) ? &img_key_OKi : &img_key_OK,
+ SHOW_SET_BLK );
+}
+
+//+============================================================================ ========================================
+static
+bool classic_keyN (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ if ((msg->input.type == InputTypeShort) && (msg->input.key == InputKeyLeft)) {
+ sceneSet(state, SCENE_CLASSIC);
+ used = true;
+ }
+
+ // Calibration keys
+ if (!used) used = key_calib(msg, state) ;
+
+ return used;
+}
+
+//+============================================================================ ========================================
+bool classic_key (const eventMsg_t* const msg, state_t* const state)
+{
+ // Classic controllers have TWO scenes
+ if (state->scene == SCENE_CLASSIC_N) return classic_keyN(msg, state) ;
+
+ // Default scene
+ int used = false; // assume key is NOT-handled
+
+ switch (msg->input.type) {
+ case InputTypeShort: //# input.key) {
+ case InputKeyUp: //#
+#include
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Classic Controller ... Classic Controller Pro is electronically the same
+//
+// ANA{l} ANA{r}
+// BTN{l} BTN{L} BTN{R} BTN{r}
+// ,--------. ,-, ,-, .--------,
+// .----------------------------------------------------------.
+// | |
+// | BTN{W} BTN{x} |
+// | BTN{A} BTN{D} BTN{-} BTN{h} BTN{+} BTN{y} BTN{a} |
+// | BTN{S} BTN{b} |
+// | |
+// | ANA{y} ANA{Y} |
+// | ANA{x} ANA{X} |
+// | |
+// `----------------------------------------------------------'
+//
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Controllers which have calibration must have their calibratable controls here
+//! Is there a better way to get the start of the decode struct to match the calibration struct ?
+#define CLASSIC_ANALOGUE \
+ uint8_t trgZL, trgZR; /* ANA{l, l} lowercase=trigger 5bit values {5} */ \
+ uint8_t joyLX, joyLY; /* ANA{x, y} left=lowercase 6bit values {6}<-- */ \
+ uint8_t joyRX, joyRY; /* ANA{X, Y} 5bit values {5} */
+
+//-----------------------------------------------------------------------------
+// Calibratable controls
+//
+typedef
+ struct ecCalClassic
+ {
+ CLASSIC_ANALOGUE
+ }
+ecCalClassic_t;
+
+//-----------------------------------------------------------------------------
+// All controls
+//
+typedef
+ struct ecDecClassic
+ {
+ CLASSIC_ANALOGUE // MUST be first
+
+ // Digital controls
+ bool btnZL, btnZR; // BTN{l, l}
+
+ bool btnL, btnR; // BTN{L, R} upperrcase=shoulder
+
+ bool padU, padL, padD, padR; // BTN{W, A, S, D}
+
+ bool btnM, btnH, btnP; // BTN{-, h, +}
+
+ bool btnX, btnY; // BTN{x, y}
+ bool btnA, btnB; // BTN{a, b}
+
+ }
+ecDecClassic_t;
+
+#undef CLASSIC_ANALOGUE
+
+//============================================================================= ========================================
+// Function prototypes
+//
+#include // Canvas
+typedef struct wiiEC wiiEC_t ;
+typedef enum ecCalib ecCalib_t ;
+typedef struct state state_t ;
+typedef struct eventMsg eventMsg_t ;
+
+void classic_decode (wiiEC_t* const pec) ;
+void classic_msg (wiiEC_t* const pec, FuriMessageQueue* const queue) ;
+void classic_calib (wiiEC_t* const pec, ecCalib_t c) ;
+
+void classic_show (Canvas* const canvas, state_t* const state) ;
+bool classic_key (const eventMsg_t* const msg, state_t* const state) ;
+
+#endif //WII_EC_CLASSIC_H_
diff --git a/applications/plugins/wii_ec_anal/wii_ec_macros.h b/applications/plugins/wii_ec_anal/wii_ec_macros.h
new file mode 100644
index 000000000..33daf944d
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec_macros.h
@@ -0,0 +1,84 @@
+#ifndef WII_EC_MACROS_H_
+#define WII_EC_MACROS_H_
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// CHECK MACROS
+//
+// I don't generally like this style of coding - it just (generally) makes things nightmarish to debug
+// However, on this occasion I think it's a good choice (to make adding controllers LESS bug-prone)
+//
+
+//if (furi_message_queue_get_count(queue) > 18) WARN("queue high %d", furi_message_queue_get_count(queue));
+#define MSGQ(lbl) do { \
+ msg.wiiEc.in = lbl; \
+ furi_message_queue_put(queue, &msg, 0); \
+}while(0)
+
+
+// A 'standard' "button" is an independent SPST switch
+// Eg. Nunchuck 'Z' button
+// The "value" will always be 0
+#define BUTTON(btn,lbl) do { \
+ if (new->btn != old->btn) { \
+ msg.wiiEc.type = (new->btn) ? WIIEC_PRESS : WIIEC_RELEASE; \
+ msg.wiiEc.val = 0; \
+ MSGQ(lbl); \
+ } \
+}while(0)
+
+// An "analogue button" is an SPST coupled with an ananlogue 'switch'
+// Eg. The "bottom out" switches on the triggers of the classic controller
+// The "value" will be the value of the associated analogue controller
+#define ANABTN(btn,ana,lbl) do { \
+ if (new->btn != old->btn) { \
+ msg.wiiEc.type = (new->btn) ? WIIEC_PRESS : WIIEC_RELEASE; \
+ msg.wiiEc.val = new->ana; \
+ MSGQ(lbl); \
+ } \
+}while(0)
+
+#define ANALOG(ana,lbl) do { \
+ if (new->ana != old->ana) { \
+ msg.wiiEc.type = WIIEC_ANALOG; \
+ msg.wiiEc.val = new->ana; \
+ MSGQ(lbl); \
+ } \
+}while(0)
+
+#define ACCEL(acc,lbl) do { \
+ if (new->acc != old->acc) { \
+ msg.wiiEc.type = WIIEC_ACCEL; \
+ msg.wiiEc.val = new->acc; \
+ MSGQ(lbl); \
+ } \
+}while(0)
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// CALIBRATION MACROS
+//
+// Again ...I totally agree with anyone who says "MACRO coding" is (gernally) a poor choice of programming style
+// But something about this code is making it soooo appealing
+//
+// ... v=variable, n=number
+//
+#define FACTORY_LO(v,n) do{ (dst[1]. v) = n; }while(0)
+#define FACTORY_MID(v,n) do{ (dst[2]. v) = n; }while(0)
+#define FACTORY_HI(v,n) do{ (dst[3]. v) = n; }while(0)
+
+#define TRACK_LO(v) do{ if ((src-> v) < (dst[0]. v)) (dst[0]. v) = (src-> v); }while(0)
+#define TRACK_HI(v) do{ if ((src-> v) > (dst[4]. v)) (dst[4]. v) = (src-> v); }while(0)
+#define TRACK_LO_HI(v) do{ TRACK_LO(v); TRACK_HI(v); }while(0)
+
+#define RESET_LO(v,b) do{ (dst[0]. v) = (dst[1]. v) = ((1<<(b))-1); }while(0)
+#define RESET_HI(v) do{ (dst[4]. v) = (dst[3]. v) = 0; }while(0)
+#define RESET_MID(v) do{ (dst[2]. v) = (src-> v); }while(0)
+#define RESET_LO_HI(v,b) do{ RESET_LO(v,b); RESET_HI(v); }while(0)
+#define RESET_LO_MID_HI(v,b) do{ RESET_LO(v,b); RESET_MID(v); RESET_HI(v); }while(0)
+
+#define RANGE_LO(v) do{ if ((src-> v) < (dst[1]. v)) (dst[1]. v) = (src-> v); }while(0)
+#define RANGE_HI(v) do{ if ((src-> v) > (dst[3]. v)) (dst[3]. v) = (src-> v); }while(0)
+#define RANGE_LO_HI(v) do{ RANGE_LO(v); RANGE_HI(v); }while(0)
+
+#define CENTRE(v) do{ (dst[2]. v) = (src-> v); } while(0)
+
+#endif //WII_EC_MACROS_H_
diff --git a/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c b/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c
new file mode 100644
index 000000000..81c32a243
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c
@@ -0,0 +1,459 @@
+#include
+#include // Core API
+
+#include "wii_anal.h"
+#include "wii_i2c.h"
+#include "bc_logging.h"
+
+#include "gfx/images.h" // Images
+#include "wii_anal_lcd.h" // Drawing functions
+#include "wii_anal_keys.h" // key mappings
+
+// ** If you want to see what this source code looks like with all the MACROs expanded
+// ** grep -v '#include ' wii_ec_nunchuck.c | gcc -E -o /dev/stdout -xc -
+# include "wii_ec_macros.h"
+
+//+============================================================================ ========================================
+// Standard Nunchuck : 2 buttons, 1 analogue joystick, 1 3-axis accelerometer
+//
+void nunchuck_decode (wiiEC_t* const pec)
+{
+ ecDecNunchuck_t* p = &pec->dec[(pec->decN = !pec->decN)].nunchuck;
+ uint8_t* joy = pec->joy;
+
+ p->btnC = !(joy[5] & 0x02); // !{1}
+ p->btnZ = !(joy[5] & 0x01); // !{1}
+
+ p->joyX = joy[0]; // {8}
+ p->joyY = joy[1]; // {8}
+
+ p->accX = ((uint16_t)joy[2] << 2) | ((joy[5] >>2) & 0x03); // {10}
+ p->accY = ((uint16_t)joy[3] << 2) | ((joy[5] >>4) & 0x03); // {10}
+ p->accZ = ((uint16_t)joy[4] << 2) | ((joy[5] >>6) & 0x03); // {10}
+
+ DEBUG(">%d> C:%c, Z:%c, Joy{x:%02X, y:%02X}, Acc{x:%03X, y:%03X, z:%03X}", pec->decN,
+ (p->btnC ? '#' : '.'), (p->btnZ ? '#' : '.'),
+ p->joyX, p->joyY, p->accX, p->accY, p->accZ
+ );
+}
+
+//+============================================================================ ========================================
+// Give each button a unique character identifier
+//
+void nunchuck_msg (wiiEC_t* const pec, FuriMessageQueue* const queue)
+{
+ ecDecNunchuck_t* new = &pec->dec[pec->decN].nunchuck;
+ ecDecNunchuck_t* old = &pec->dec[!pec->decN].nunchuck;
+
+ eventMsg_t msg = {
+ .id = EVID_WIIEC,
+ .wiiEc = {
+ .type = WIIEC_NONE,
+ .in = ' ',
+ .val = 0,
+ }
+ };
+
+ BUTTON(btnC, 'c');
+ BUTTON(btnZ, 'z');
+
+ ANALOG(joyX, 'x');
+ ANALOG(joyY, 'y');
+
+ ACCEL(accX, 'x');
+ ACCEL(accY, 'y');
+ ACCEL(accZ, 'z');
+}
+
+//+============================================================================ ========================================
+// https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-5--read-actual-calibration-data-from-the-device-14
+//
+void nunchuck_calib (wiiEC_t* const pec, ecCalib_t c)
+{
+ ecDecNunchuck_t* src = &pec->dec[pec->decN].nunchuck; // from input
+ ecCalNunchuck_t* dst = pec->calS.nunchuck; // to calibration data
+
+ if (c & CAL_RESET) { // initialise ready for software calibration
+ // LO is set to the MAXIMUM value (so it can be reduced)
+ // HI is set to ZERO (so it can be increased)
+ RESET_LO_HI(accX, 10); // 10bit value
+ RESET_LO_HI(accY, 10); // 10bit value
+ RESET_LO_HI(accZ, 10); // 10bit value
+
+ RESET_LO_HI(joyX, 8); // 8bit value
+ RESET_LO_HI(joyY, 8); // 8bit value
+ }
+ if (c & CAL_FACTORY) { // (re)set to factory defaults
+ //! "[4] LSB of Zero value of X,Y,Z axes" ...helpful!
+ //! ...Well, my test nunchuck has bits set in the bottom 6 bits, so let's guess ;)
+
+ // No value available - annecdotal tests suggest 8 is reasonable
+ FACTORY_LO( accX, 8);
+ FACTORY_LO( accY, 8);
+ FACTORY_LO( accZ, 8);
+
+ // @ 0G
+ FACTORY_MID( accX, ((pec->calF[0] <<2) | ((pec->calF[3] >>4) &0x3)) ) ;
+ FACTORY_MID( accY, ((pec->calF[1] <<2) | ((pec->calF[3] >>2) &0x3)) ) ;
+ FACTORY_MID( accZ, ((pec->calF[2] <<2) | ((pec->calF[3] ) &0x3)) ) ;
+
+ // @ 1G
+ FACTORY_HI( accX, ((pec->calF[4] <<2) | ((pec->calF[7] >>4) &0x3)) ) ;
+ FACTORY_HI( accY, ((pec->calF[5] <<2) | ((pec->calF[7] >>2) &0x3)) ) ;
+ FACTORY_HI( accZ, ((pec->calF[6] <<2) | ((pec->calF[7] ) &0x3)) ) ;
+
+ // Joysticks
+ FACTORY_LO( joyX, pec->calF[ 9] ) ;
+ FACTORY_MID(joyX, pec->calF[10] ) ;
+ FACTORY_HI( joyX, pec->calF[ 8] ) ;
+
+ FACTORY_LO( joyY, pec->calF[12] ) ;
+ FACTORY_MID(joyY, pec->calF[13] ) ;
+ FACTORY_HI( joyY, pec->calF[11] ) ;
+ }
+ if (c & CAL_TRACK) { // track maximum and minimum values seen
+ TRACK_LO_HI(accX);
+ TRACK_LO_HI(accY);
+ TRACK_LO_HI(accZ);
+
+ TRACK_LO_HI(joyX);
+ TRACK_LO_HI(joyY);
+ }
+ if (c & CAL_RANGE) { // perform software calibration step
+ RANGE_LO_HI(accX);
+ RANGE_LO_HI(accY);
+ RANGE_LO_HI(accZ);
+
+ if (!(c & CAL_NOTJOY)) { // double negative!
+ RANGE_LO_HI(joyX);
+ RANGE_LO_HI(joyY);
+ }
+ }
+ if (c & CAL_CENTRE) { // reset centre point of joystick
+ CENTRE(accX);
+ CENTRE(accY);
+ CENTRE(accZ);
+
+ CENTRE(joyX);
+ CENTRE(joyY);
+ }
+}
+
+//============================================================================= ========================================
+// Accelerometer screen ...might this be useful for other controllers?
+//
+// https://bootlin.com/labs/doc/nunchuk.pdf
+// X : Move Left/Right : -left / +right
+// Y : Move Fwd/Bkwd : -fwd / +bkwd
+// Z : Move Down/Up : -down / +up
+//
+// Movement in the direction of an axis changes that axis reading
+// Twisting/tilting around an axis changes the other two readings
+//
+// EG. Move left will effect X ; turn left will effect Y & Z
+//
+#define aw 110 // axis width
+#define ah 15 // height {0......7......14}
+#define am 7 // midpoint { 7 }
+#define ar 7 // range {1234567 1234567}
+
+enum {
+ ACC_X = 0,
+ ACC_Y = 1,
+ ACC_Z = 2,
+ ACC_CNT = 3,
+ ACC_1 = ACC_X, // first
+ ACC_N = ACC_Z, // last
+};
+
+//+============================================================================
+static
+void nunchuck_showAcc (Canvas* const canvas, state_t* const state)
+{
+ ecDecNunchuck_t* d = &state->ec.dec[state->ec.decN].nunchuck;
+ ecCalNunchuck_t* lo = &state->ec.calS.nunchuck[1];
+ ecCalNunchuck_t* mid = &state->ec.calS.nunchuck[2];
+ ecCalNunchuck_t* hi = &state->ec.calS.nunchuck[3];
+
+ int y[ACC_CNT] = {0, 0+(ah+4), 0+((ah+4)*2)};
+ int x = 10;
+
+ static uint16_t v[ACC_CNT][aw] = {0};
+// static uint16_t tv[ACC_CNT][aw] = {0};
+
+ static uint16_t idx = 0;
+ static uint16_t cnt = aw -1;
+
+ // Only record when scanner NOT-paused
+ if (!state->pause) {
+ uint16_t dead = (1<<5);
+
+ // Find axes y-offsets
+ for (int a = ACC_1; a <= ACC_N; a++) {
+ uint16_t* dp = NULL; // data value (current reading)
+ uint16_t* lp = NULL; // lo value
+ uint16_t* mp = NULL; // mid value
+ uint16_t* hp = NULL; // hi value
+ uint16_t* vp = NULL; // value (result)
+
+ switch (a) {
+ case ACC_X:
+ dp = & d->accX; // data (input)
+ lp = & lo->accX; // low \.
+ mp = &mid->accX; // mid > calibration
+ hp = & hi->accX; // high /
+ vp = &v[ ACC_X][idx]; // value (where to store the result)
+ break;
+ case ACC_Y:
+ dp = & d->accY;
+ lp = & lo->accY;
+ mp = &mid->accY;
+ hp = & hi->accY;
+ vp = &v[ ACC_Y][idx];
+ break;
+ case ACC_Z:
+ dp = & d->accZ;
+ lp = & lo->accZ;
+ mp = &mid->accZ;
+ hp = & hi->accZ;
+ vp = &v[ ACC_Z][idx];
+ break;
+ default: break ;
+ }
+
+ // Again - qv. the joysick calibration:
+ // This is not the "right way" to do this, it is just "one way" to do it
+ // ...mid point and extreme zones have a deadzone
+ // ...the rest is evenly divided by the amount of space on the graph
+ if ((*dp >= (*mp -dead)) && (*dp <= (*mp +dead))) *vp = ar ;
+ else if (*dp >= (*hp -dead)) *vp = ah-1 ;
+ else if (*dp <= (*lp +dead)) *vp = 0 ;
+ else if (*dp < *mp) {
+ uint16_t min = ((*lp +dead) +1);
+ uint16_t max = ((*mp -dead) -1);
+ float range = (max -min) +1;
+ float m = range /(ar-1); // 6 evenly(/fairly) divided zones
+ *vp = ((int)((*dp -min) /m)) +1;
+
+ } else {//if (*dp > *mp)
+ uint16_t min = ((*mp +dead) +1);
+ uint16_t max = ((*hp -dead) -1);
+ float range = (max -min) +1;
+ float m = range /(ar-1); // 6 evenly(/fairly) divided zones
+ *vp = ((int)((*dp -min) /m)) +1 +ar;
+ }
+ }
+
+//! If we decide to offer "export to CSV"
+//! I suggest we keep a second array of true-values, rather than do all the maths every time
+//! Also - the data will need to me moved to the 'state' table - so a.n.other function can save it off
+// tv[ACC_X][idx] = d->accX;
+// tv[ACC_Y][idx] = d->accY;
+// tv[ACC_Z][idx] = d->accZ;
+
+ // Prepare for the next datapoint
+ if (++idx >= aw) idx = 0 ;
+ if (cnt) cnt-- ;
+ }
+
+ // Auto-pause
+ if (state->apause && !idx) state->pause = true ;
+
+ // *** Draw axes ***
+ show(canvas, 0,y[ACC_X] +((ah -img_6x8_X.h) /2), &img_6x8_X, SHOW_SET_BLK);
+ show(canvas, 0,y[ACC_Y] +((ah -img_6x8_Y.h) /2), &img_6x8_Y, SHOW_SET_BLK);
+ show(canvas, 0,y[ACC_Z] +((ah -img_6x8_Z.h) /2), &img_6x8_Z, SHOW_SET_BLK);
+
+ canvas_set_color(canvas, ColorBlack);
+ for (int a = ACC_1; a <= ACC_N; a++) {
+ canvas_draw_line(canvas, x-1,y[a] , x -1,y[a]+ah);
+ canvas_draw_line(canvas, x ,y[a]+ah, x+aw-1,y[a]+ah);
+
+ // Mid & Peak lines
+ for (int i = 1; i < aw; i += 3) {
+ canvas_draw_dot(canvas, x+i,y[a]);
+ canvas_draw_dot(canvas, x+i,y[a] +(ah /2));
+ }
+ }
+
+ // Data (wiper display - see notes.txt for scrolling algorithm)
+ int end = idx ? idx : aw;
+ for (int a = ACC_1; a <= ACC_N; a++) {
+ canvas_draw_dot(canvas, x,y[a]+v[a][idx]);
+ for (int i = 1; i < end; i++)
+ canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]);
+ if (!state->apause)
+ for (int i = end+10; i < aw -cnt; i++)
+ canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]);
+ }
+ // Wipe bar
+ if (end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1);
+ if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1);
+ if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1);
+
+ // *** Mode buttons ***
+ show(canvas, 0,55, &img_key_L, SHOW_SET_BLK); // mode key
+
+ if ((state->calib & CAL_RANGE) || state->pause) state->flash++ ;
+
+ // -pause- ...yeah, this got a little out of hand! LOL!
+ if (state->pause || state->apause) {
+ if (state->pause && state->apause && !idx) {
+ if (state->flash &8) {
+ show(canvas, 108,56, &img_key_U, SHOW_SET_BLK);
+ } else {
+ show(canvas, 108,56, &img_key_Ui, SHOW_SET_BLK);
+ canvas_draw_line(canvas, x+aw,y[0], x+aw,y[2]+ah-1);
+ }
+ } else {
+ show(canvas, 108,56, &img_key_Ui, SHOW_SET_BLK);
+ }
+ } else {
+ show(canvas, 108,56, &img_key_U, SHOW_SET_BLK); // pause
+ }
+
+ // -calibration-
+ if (state->calib & CAL_RANGE) {
+ show(canvas, 119,55, (state->flash &8) ? &img_key_OKi : &img_key_OK, SHOW_SET_BLK);
+ } else {
+ show(canvas, 119,55, &img_key_OK, SHOW_SET_BLK);
+ }
+}
+
+# undef aw
+# undef ah
+# undef am
+# undef ar
+
+//+============================================================================ ========================================
+// Default nunchuck screen
+//
+void nunchuck_show (Canvas* const canvas, state_t* const state)
+{
+ // Nunchucks have TWO scenes
+ if (state->scene == SCENE_NUNCHUCK_ACC) return nunchuck_showAcc(canvas, state) ;
+
+ // Default scene
+ ecDecNunchuck_t* d = &state->ec.dec[state->ec.decN].nunchuck;
+ ecCalNunchuck_t* c = (state->hold) ? &state->ec.calS.nunchuck[(state->hold < 0) ? 0 : 4]
+ : (ecCalNunchuck_t*)d ; //! danger will robinson!
+ ecCalNunchuck_t* js = state->ec.calS.nunchuck;
+
+ // X, Y, Z
+ show(canvas, 42,0, &img_6x8_X, SHOW_SET_BLK);
+ show(canvas, 73,0, &img_6x8_Y, SHOW_SET_BLK);
+ show(canvas, 104,0, &img_6x8_Z, SHOW_SET_BLK);
+
+ canvas_draw_str_aligned(canvas, 0,14, AlignLeft, AlignTop, "Accel");
+ canvas_draw_str_aligned(canvas, 0,28, AlignLeft, AlignTop, "Joy");
+
+ // accel values
+ showHex(canvas, 34,12, c->accX, 3,2);
+ showHex(canvas, 65,12, c->accY, 3,2);
+ showHex(canvas, 96,12, c->accZ, 3,2);
+ // Joy values
+ showHex(canvas, 38,27, c->joyX, 2,2);
+ showHex(canvas, 69,27, c->joyY, 2,2);
+
+ showJoy(canvas, 103,32, js[1].joyX, js[2].joyX, js[3].joyX,
+ js[1].joyY, js[2].joyY, js[3].joyY, d->joyX,d->joyY, 8);
+
+ // buttons
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str_aligned(canvas, 0,44, AlignLeft, AlignTop, "Button");
+
+ if (!d->btnC) {
+ canvas_draw_rframe(canvas, 36,42, 18,12, 6);
+ show(canvas, 42,44, &img_6x8_C, SHOW_SET_BLK);
+ } else {
+ canvas_draw_rbox(canvas, 36,42, 18,12, 6);
+ show(canvas, 42,44, &img_6x8_C, SHOW_SET_WHT);
+ canvas_set_color(canvas, ColorBlack);
+ }
+
+ if (!d->btnZ) {
+ canvas_draw_rframe(canvas, 64,40, 24,16, 2);
+ show(canvas, 73,44, &img_6x8_Z, SHOW_SET_BLK);
+ } else {
+ canvas_draw_rbox(canvas, 64,40, 24,16, 2);
+ show(canvas, 73,44, &img_6x8_Z, SHOW_SET_WHT);
+ }
+
+ // Navigation
+ showPeakHold(state, canvas, state->hold); // peak keys
+ show(canvas, 0,55, &img_key_L, SHOW_SET_BLK); // mode keys
+ show(canvas, 9,55, &img_key_R, SHOW_SET_BLK);
+}
+
+//+============================================================================ ========================================
+static
+bool nunchuck_keyAcc (const eventMsg_t* const msg, state_t* const state)
+{
+ int used = false; // assume key is NOT-handled
+
+ switch (msg->input.type) {
+ case InputTypeShort: //# input.key) {
+ case InputKeyDown: //# pause) state->pause = false ; // Paused? Restart
+ else state->apause = !state->apause ; // No? toggle auto-pause
+ used = true;
+ break;
+
+ case InputKeyLeft: //# calib &= ~CAL_NOTJOY; // DO calibrate joystick in NUNCHUCK mode
+ used = true;
+ break;
+
+ default: break ; //#
+ }
+ break;
+
+ default: break ;
+ }
+
+ // Calibration keys
+ if (!used) used = key_calib(msg, state) ;
+
+ return used;
+}
+
+//+============================================================================ ========================================
+bool nunchuck_key (const eventMsg_t* const msg, state_t* const state)
+{
+ // Nunchucks have TWO scenes
+ if (state->scene == SCENE_NUNCHUCK_ACC) return nunchuck_keyAcc(msg, state) ;
+
+ // Default scene
+ int used = false; // assume key is NOT-handled
+
+ switch (msg->input.type) {
+ case InputTypeShort: //# input.key) {
+ case InputKeyLeft: //# calib |= CAL_NOTJOY; // do NOT calibrate joystick in _ACC mode
+ used = true;
+ break;
+ default: break ; //#
+ }
+ break;
+
+ default: break ;
+ }
+
+ // Calibration keys
+ if (!used) used = key_calib(msg, state) ;
+
+ return used;
+}
diff --git a/applications/plugins/wii_ec_anal/wii_ec_nunchuck.h b/applications/plugins/wii_ec_anal/wii_ec_nunchuck.h
new file mode 100644
index 000000000..020ecd555
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec_nunchuck.h
@@ -0,0 +1,53 @@
+#ifndef WII_EC_NUNCHUCK_H_
+#define WII_EC_NUNCHUCK_H_
+
+#include
+#include
+
+//-----------------------------------------------------------------------------
+// Controllers which have calibration must have their calibratable controls here
+//! Is there a better way to get the start of the decode struct to match the calibration struct ?
+#define NUNCHUCK_ANALOGUE \
+ uint8_t joyX, joyY; \
+ uint16_t accX, accY, accZ;
+
+//-----------------------------------------------------------------------------
+// Calibratable controls
+//
+typedef
+ struct ecCalNunchuck {
+ NUNCHUCK_ANALOGUE
+ }
+ecCalNunchuck_t;
+
+//-----------------------------------------------------------------------------
+// All controls
+//
+typedef
+ struct ecDecNunchuck {
+ NUNCHUCK_ANALOGUE // MUST be first
+
+ // Digital controls
+ bool btnC, btnZ; // BTN{c, z}
+ }
+ecDecNunchuck_t;
+
+#undef NUNCHUCK_ANALOGUE
+
+//=============================================================================
+// Function prototypes
+//
+#include // Canvas
+typedef struct wiiEC wiiEC_t ;
+typedef enum ecCalib ecCalib_t ;
+typedef struct state state_t ;
+typedef struct eventMsg eventMsg_t ;
+
+void nunchuck_decode (wiiEC_t* const pec) ;
+void nunchuck_msg (wiiEC_t* const pec, FuriMessageQueue* const queue) ;
+void nunchuck_calib (wiiEC_t* const pec, ecCalib_t c) ;
+
+void nunchuck_show (Canvas* const canvas, state_t* const state) ;
+bool nunchuck_key (const eventMsg_t* const msg, state_t* const state) ;
+
+#endif //WII_EC_NUNCHUCK_H_
diff --git a/applications/plugins/wii_ec_anal/wii_ec_udraw.c b/applications/plugins/wii_ec_anal/wii_ec_udraw.c
new file mode 100644
index 000000000..babbc92ab
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec_udraw.c
@@ -0,0 +1,145 @@
+//! udraw support is NOT written - this is just notes about the init function
+#include
+#include // Core API
+
+#include "wii_anal.h"
+#include "wii_ec.h"
+#include "bc_logging.h"
+
+#include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header]
+
+// ** If you want to see what this source code looks like with all the MACROs expanded
+// ** grep -v '#include ' wii_ec_udraw.c | gcc -E -o /dev/stdout -xc -
+# include "wii_ec_macros.h"
+
+//+============================================================================ ========================================
+// https://github.com/madhephaestus/WiiChuck/blob/master/src/Drawsome.cpp#L3
+// Gratuitously stolen ... never tested (don't own one) - just bought one on ebay
+// although it seems like the UK version is a "uDraw" and MIGHT contain a different chipset :/
+//
+// read 6 bytes starting from 0x20
+// read 6 bytes starting from 0x28
+// read 6 bytes starting from 0x30
+// read 6 bytes starting from 0x38
+// read 6 bytes starting from 0x00 (#1)
+// read 6 bytes starting from 0x00 (#2)
+// write 1 byte [0x01] to 0xFB
+// read 6 bytes starting from 0x00 (#3)
+// read 6 bytes starting from 0x00 (#4)
+//
+bool udraw_init (wiiEC_t* const pec)
+{
+ ENTER;
+ bool rv = true;
+
+(void)pec;
+/*
+//! this is the Drawsome code, NOT the uDraw code !!
+ static const uint8_t reg[9] = {0x20, 0x28, 0x30, 0x38, 0x00, 0x00, 0xFB, 0x00, 0x00}; // 0..8
+ const uint8_t* p = reg;
+ uint8_t buf[6] = {0};
+
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 0
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 1
+ furi_delay_ms(100);
+
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 2
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 3
+ furi_delay_ms(100);
+
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 4
+ furi_delay_ms(100);
+
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 5
+ furi_delay_ms(100);
+
+ buf[0] = *p++;
+ buf[1] = 0x01;
+ if (!furi_hal_i2c_tx(bus,addr, buf,2, timeout)) goto fail ; // 6
+
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 7
+ furi_delay_ms(100);
+
+ if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 8
+ furi_delay_ms(100);
+
+ TRACE("%s : OK #%d", __func__, (p-reg));
+ goto done;
+
+fail:
+ ERROR("%s : fail #%d", __func__, (p -reg) -1);
+ rv = false;
+
+done:
+*/
+ LEAVE;
+ return rv;
+}
+
+//+============================================================================ ========================================
+bool udraw_key (const eventMsg_t* const msg, state_t* const state)
+{
+(void)state;
+ bool run = true;
+
+ switch (msg->input.type) {
+ case InputTypeShort: //# input.key) {
+ case InputKeyUp: //# ! After INPUT_LONG_PRESS interval, asynch to InputTypeRelease
+ switch (msg->input.key) {
+ case InputKeyUp: //# >U [ LONG-UP ]
+ case InputKeyDown: //# >D [ LONG-DOWN ]
+ case InputKeyLeft: //# >L [ LONG-LEFT ]
+ case InputKeyRight: //# >R [ LONG-RIGHT ]
+ case InputKeyOk: //# >O [ LONG-OK ]
+ case InputKeyBack: //# >B [ LONG-BACK ]
+ default: break ; //# >?
+ }
+ break;
+ case InputTypePress: //# +! After debounce
+ switch (msg->input.key) {
+ case InputKeyUp: //# +U [ SHORT-UP ]
+ case InputKeyDown: //# +D [ SHORT-DOWN ]
+ case InputKeyLeft: //# +L [ SHORT-LEFT ]
+ case InputKeyRight: //# +R [ SHORT-RIGHT ]
+ case InputKeyOk: //# +O [ SHORT-OK ]
+ case InputKeyBack: //# +B [ SHORT-BACK ]
+ default: break ; //# +?
+ }
+ break;
+ case InputTypeRepeat: //# *! With INPUT_REPEATE_PRESS period after InputTypeLong event
+ switch (msg->input.key) {
+ case InputKeyUp: //# *U [ REPEAT-UP ]
+ case InputKeyDown: //# *D [ REPEAT-DOWN ]
+ case InputKeyLeft: //# *L [ REPEAT-LEFT ]
+ case InputKeyRight: //# *R [ REPEAT-RIGHT ]
+ case InputKeyOk: //# *O [ REPEAT-OK ]
+ case InputKeyBack: //# *B [ REPEAT-BACK ]
+ default: break ; //# *?
+ }
+ break;
+ case InputTypeRelease: //# -! After debounce
+ switch (msg->input.key) {
+ case InputKeyUp: //# -U [ RELEASE-UP ]
+ case InputKeyDown: //# -D [ RELEASE-DOWN ]
+ case InputKeyLeft: //# -L [ RELEASE-LEFT ]
+ case InputKeyRight: //# -R [ RELEASE-RIGHT ]
+ case InputKeyOk: //# -O [ RELEASE-OK ]
+ case InputKeyBack: //# -B [ RELEASE-BACK ]
+ default: break ; //# -?
+ }
+ break;
+ default: return true ;
+ }
+
+ return run;
+}
diff --git a/applications/plugins/wii_ec_anal/wii_ec_udraw.h b/applications/plugins/wii_ec_anal/wii_ec_udraw.h
new file mode 100644
index 000000000..1721894e5
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_ec_udraw.h
@@ -0,0 +1,18 @@
+#ifndef WII_EC_UDRAW_H_
+#define WII_EC_UDRAW_H_
+
+#include
+#include
+
+//============================================================================= =======================================
+// Function prototypes
+//
+typedef struct wiiEC wiiEC_t ;
+typedef enum ecCalib ecCalib_t ;
+typedef struct eventMsg eventMsg_t ;
+typedef struct state state_t ;
+
+bool udraw_init (wiiEC_t* const pec) ;
+bool udraw_key (const eventMsg_t* const msg, state_t* const state) ;
+
+#endif //WII_EC_UDRAW_H_
diff --git a/applications/plugins/wii_ec_anal/wii_i2c.c b/applications/plugins/wii_ec_anal/wii_i2c.c
new file mode 100644
index 000000000..90fe22163
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_i2c.c
@@ -0,0 +1,309 @@
+//----------------------------------------------------------------------------- ----------------------------------------
+// Biblio: [standing on the shoulders of giants]
+// https://bootlin.com/labs/doc/nunchuk.pdf
+// https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-i2c-protocol-9
+// https://web.archive.org/web/20220000000000*/https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254
+// https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L14
+// https://wiibrew.org/wiki/Wiimote/Extension_Controllers
+// https://www.best-microcontroller-projects.com/i2c-tutorial.html
+//
+// WiiMote Extension Controller:
+// Bus Address : 0x52
+// Register autoincrements after each (byte is) read
+// 0x00..0x05 ( 6 bytes) ... [r] Controller Data
+// 0x20..0x2F (16 bytes) ... [r] Calibration Data
+// 0x30..0x3F (16 bytes) ... [r] (A copy of the) Calibration Data
+// 0x40..0x4F (16 bytes) ... [w] Encryption key(s)
+// 0xFA..0xFF ( 6 bytes) ... [r] Perhipheral ID
+
+//----------------------------------------------------------------------------- ----------------------------------------
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header]
+
+#include "wii_anal.h"
+#include "wii_i2c.h"
+#include "wii_ec.h"
+
+#include "bc_logging.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// Wii Extension Controller i2c Bus address
+static const uint8_t ec_i2cAddr = 0x52;
+
+// Initialise for UNencrypted comms
+static const uint8_t regInit1 = 0xF0;
+static const uint8_t regInit2 = 0xFB;
+static const uint8_t cmdInit1[] = {regInit1, 0x55};
+static const uint8_t cmdInit2[] = {regInit2, 0x00};
+
+// Initialise for ENcrypted comms
+static const uint8_t regInitEnc = 0x40;
+static const uint8_t cmdInitEnc[] = {regInitEnc, 0x00};
+
+// Crypto key (PSK), base register : {0x40..0x4F}[2][8]
+static const uint8_t regEnc = 0x40; // ENC_LEN
+
+// Controller State data, base register : {0x00..0x05}[6]
+static const uint8_t regJoy = 0x00; // JOY_LEN
+
+// Calibration data, base register : {0x20..0x2F}[16]
+static const uint8_t regCal = 0x20; // CAL_LEN
+
+// Controller ID, base register : {0xFA..0xFF}[6]
+static const uint8_t regPid = 0xFA; // PID_LEN
+
+//+============================================================================ ========================================
+// Hexdump a buffer to the logfile
+//
+#if LOG_LEVEL >= 4 // INFO
+
+static
+void dump (const uint8_t* buf, const unsigned int len, const char* id)
+{
+ // snprintf() would be useful!
+ char s[128] = {0};
+ char* p = NULL;
+
+ strcpy(s, id);
+ p = s +strlen(s);
+ *p++ = ':';
+ *p++ = ' ';
+ *p++ = '{';
+
+ for (unsigned int i = 0; i < len; i++) {
+ uint8_t hi = (buf[i] &0xF0) >>4;
+ uint8_t lo = (buf[i] &0x0F);
+
+ hi = hi + ((hi > 9) ? ('A' -10) : '0');
+ lo = lo + ((lo > 9) ? ('A' -10) : '0');
+
+ *p++ = (char)hi;
+ *p++ = (char)lo;
+ *p++ = ',';
+ }
+ *p = '\0';
+ *--p = '}';
+ INFO(s);
+}
+
+#else
+# define dump(...)
+#endif
+
+//+============================================================================ ========================================
+//
+//! -W-A-R-N-I-N-G- : THIS ENCRYPTION CODE SHOULD NEVER BE REQUIRED ... AS SUCH, I'VE NEVER TESTED IT
+//
+static
+void decrypt (uint8_t* buf, const uint8_t* encKey, const uint8_t reg, unsigned int len)
+{
+#if 1 // Use standard algorithm
+ // decrypted_byte = (encrypted_byte XOR encKey[1][address%8]) + encKey[2][address%8]
+ for (uint8_t* p = buf; p < buf+len; p++)
+ *p = (*p ^ encKey[(reg +(p -buf)) %8]) + encKey[8 +((reg +(p -buf)) %8)];
+
+#else //! This is (I think) a shortcut for an all-zero key [not tested]
+ (void)encKey;
+ (void)reg;
+ for (uint8_t* p = buf; p < buf+len; p++)
+ *p = (*p ^ 0x17) + 0x17;
+#endif
+}
+
+//+============================================================================ ========================================
+// Read the Extension Controller state
+// ...and decode it in to something sane
+//
+// Returns: {0:OK, >0:Error}
+//
+int ecRead (wiiEC_t* pec)
+{
+ ENTER;
+ int rv = 0; // assume success
+
+ if (!pec->init) {
+ WARN("%s : device not initialised", __func__);
+ rv = 1;
+ goto bail;
+ }
+
+ if (!furi_hal_i2c_is_device_ready(i2cBus,i2cAddr, i2cTimeout)) {
+ INFO("%s : device disconnected", __func__);
+ pec->init = false;
+ rv = 2;
+ goto bail;
+ }
+
+ if (!furi_hal_i2c_trxd(i2cBus,i2cAddr, ®Joy,1, pec->joy,JOY_LEN, i2cTimeout,i2cReadWait)) {
+ ERROR("%s : trxd fail", __func__);
+ rv = 3;
+ goto bail;
+ }
+
+ if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN) ;
+
+ // Decode the readings (according to Controller type)
+ ecDecode(pec);
+
+bail:
+ LEAVE;
+ return rv;
+}
+
+//+============================================================================ ========================================
+// Initialise an Extension Controller
+//
+//! To disable encryption, pass a NULL encryption key <-- this is currently ALWAYS the case
+//
+bool ecInit (wiiEC_t* pec, const uint8_t* encKey)
+{
+ ENTER;
+
+ bool rv = false; // assume failure
+
+#if 0 //! i2c workaround
+ //! I think this is done during OS startup - long before the plugin starts
+ furi_hal_i2c_init();
+#endif
+
+#if 0 //! i2c workaround
+ // May become relevant when the i2c issues are resolved
+ // Take control of the i2c bus [which returns void !?]
+ // --> firmware/targets/f7/furi_hal/furi_hal_i2c.c
+ furi_hal_i2c_acquire(i2cBus);
+#endif
+
+ pec->init = false; // assume failure
+
+ // === See if the device is alive ===
+ if (!furi_hal_i2c_is_device_ready(i2cBus,i2cAddr, i2cTimeout)) {
+ TRACE("%s : waiting for device", __func__);
+ goto bail;
+ }
+ INFO("%s : device connected", __func__);
+
+ // === Initialise the device ===
+ pec->init = false; // This goes true AFTER the (optional) controller-specific init code
+
+ // === Start the Extension Controller ===
+ if (encKey) { //! start in encrypted mode
+
+ //! todo - should this happen here, or AFTER we've got the ID ?
+
+ } else {
+
+ if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInit1,sizeof(cmdInit1), i2cTimeout) ) {
+ ERROR("%s : init fail (dec1)", __func__);
+ goto bail;
+ }
+ TRACE("%s : init OK1", __func__);
+
+ if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInit2,sizeof(cmdInit2), i2cTimeout) ) {
+ ERROR("%s : init fail (dec2)", __func__);
+ goto bail;
+ }
+ TRACE("%s : init OK2", __func__);
+ }
+
+ // === Retrieve the Extension Controller ID ===
+ if (!furi_hal_i2c_trx(i2cBus,i2cAddr, ®Pid,1, pec->pid,PID_LEN, i2cTimeout)) {
+ ERROR("%s : T(R)x fail (pid)", __func__);
+ goto bail;
+ }
+ if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN);
+ dump(pec->pid, PID_LEN, "pid"); // debug INFO
+
+ // Find the StringID in the lookup table
+ for (pec->pidx = PID_FIRST; pec->pidx < PID_ERROR; pec->pidx++)
+ if (memcmp(pec->pid, ecId[pec->pidx].id, PID_LEN) == 0) break ;
+ if (pec->pidx == PID_ERROR) pec->pidx = PID_UNKNOWN ;
+ pec->sid = ecId[pec->pidx].name;
+ INFO("sid: %s", pec->sid);
+
+ // === (optionally) Enable encryption ===
+ if (!encKey) {
+ pec->encrypt = false;
+
+ } else { // Controller WILL encrypt ALL tranmissions
+//! this encryption code fails - should it be done earlier?
+//! as it is probably never of any use, I'm kinda loathed to spend time on it
+//! https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L138
+ uint8_t encTx[1+ENC_LEN] = {0};
+ uint8_t* ep = encTx;
+
+ pec->encrypt = true;
+
+ // ** Start the Controller in ENcrytped mode
+ if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInitEnc,sizeof(cmdInitEnc), i2cTimeout) ) {
+ ERROR("%s : init fail (enc)", __func__);
+ goto bail;
+ }
+
+ // Copy the (symmetric) encryption key to the controller state table
+ if (pec->encKey != encKey)
+ memcpy(pec->encKey, encKey, ENC_LEN);
+
+ // Build the encryption key packet
+ *ep++ = regEnc;
+ memcpy(ep, pec->encKey, ENC_LEN);
+
+ // ** Send encryption key (PSK)
+ if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, encTx,(1+ENC_LEN), i2cTimeout) ) {
+ ERROR("%s : key fail", __func__);
+ goto bail;
+ }
+
+ TRACE("%s : init OK (enc)", __func__);
+ }
+
+ // === Some devices [eg. Drawsome/uDraw] require additional init code ===
+ if ( ecId[pec->init].init && (ecId[pec->init].init(pec) == false) ) goto bail ;
+ pec->init = true;
+
+ // === Read calibration data ===
+ if (!furi_hal_i2c_trx(i2cBus,i2cAddr, ®Cal,1, pec->calF,CAL_LEN, i2cTimeout)) {
+ ERROR("%s : trx fail (cal)", __func__);
+ goto bail;
+ }
+ if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN);
+ dump(pec->calF, CAL_LEN, "cal");
+
+ ecCalibrate(pec, CAL_RESET | CAL_FACTORY); // Load factory default calibration
+
+ // === Initialise decode buffers ===
+ pec->decN = 0; // read in to decode[1] (yes, N=0 -> read in to dec[1])
+ switch (ecRead(pec)) {
+ case 0: // read OK
+ memcpy(&pec->dec[0], &pec->dec[1], sizeof(pec->dec[0]));
+ dump(pec->joy, JOY_LEN, "joy");
+ break;
+
+ default: // bug: unknown
+ case 1: // bug: not initialised - should never happen
+ ERROR("%s : read bug", __func__);
+ break;
+
+ case 2: // device gone
+ case 3: // read fail
+ // Logging done by ecRead()
+ pec->init = false;
+ goto bail;
+ }
+
+ rv = true; // yay :)
+
+bail:
+#if 0 //! i2c workaround
+ furi_hal_i2c_release(i2cBus);
+#endif
+
+ LEAVE;
+ return rv;
+}
diff --git a/applications/plugins/wii_ec_anal/wii_i2c.h b/applications/plugins/wii_ec_anal/wii_i2c.h
new file mode 100644
index 000000000..18cb5ee9b
--- /dev/null
+++ b/applications/plugins/wii_ec_anal/wii_i2c.h
@@ -0,0 +1,42 @@
+#ifndef WII_I2C_H_
+#define WII_I2C_H_
+
+#include
+
+//#include "wii_ec.h"
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// i2c bus details
+//
+// https://www.best-microcontroller-projects.com/i2c-tutorial.html
+// https://web.archive.org/web/20220000000000*/https://www.best-microcontroller-projects.com/i2c-tutorial.html
+// https://training.ti.com/introduction-i2c-reserved-addresses
+//
+// After the (special) START "bit"...
+// the first 8bits (byte) of i2c data are the 7bit i2c Address,
+// FOLLOWED by 1bit to signify a READ or WRITE {0=write, 1=read}
+// The data is transmitted BIG-Endian, IE. MSb first [human readable]
+// So the address actually lives in the TOP (MSb's) of the first "byte", (with bit0 being used as the read/write flag)
+//
+// The read() and write() functions on the FZ will set the LSb appropriately,
+// BUT they do NOT shift the address left to make room for it!
+// So the address you give to read/write() MUST be given as (7bitAddress << 1)
+//
+// When we read: After we send the read command, we wait for i2cReadWait uS before reading the data
+//
+
+// firmware/targets/f7/furi_hal/furi_hal_i2c_types.h
+#define i2cBus (&furi_hal_i2c_handle_external) // FZ external i2c bus
+#define i2cAddr (ec_i2cAddr << 1)
+#define i2cTimeout (3) // in mS
+#define i2cReadWait (300) //! 300uS: how low can we take this?
+
+//----------------------------------------------------------------------------- ----------------------------------------
+// public functions
+//
+typedef struct wiiEC wiiEC_t ;
+
+bool ecInit (wiiEC_t* const pec, const uint8_t* encKey) ;
+int ecRead (wiiEC_t* const pec) ;
+
+#endif //WII_I2C_H_