Merge pull request #2237 from socram8888/waveshare-gd

[WIP] Use GDlib in Waveshare ePapers command
This commit is contained in:
Iceman 2024-01-07 21:51:46 +01:00 committed by GitHub
commit 18fd68900d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 458 additions and 541 deletions

View file

@ -35,7 +35,7 @@ jobs:
run: brew tap RfidResearchGroup/proxmark3 run: brew tap RfidResearchGroup/proxmark3
- name: Install dependencies - name: Install dependencies
run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl gd
continue-on-error: true continue-on-error: true
- name: Install Python dependencies - name: Install Python dependencies
@ -75,7 +75,7 @@ jobs:
run: brew tap RfidResearchGroup/proxmark3 run: brew tap RfidResearchGroup/proxmark3
- name: Install dependencies - name: Install dependencies
run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl gd
continue-on-error: true continue-on-error: true
- name: Install Python dependencies - name: Install Python dependencies
@ -116,7 +116,7 @@ jobs:
run: brew tap RfidResearchGroup/proxmark3 run: brew tap RfidResearchGroup/proxmark3
- name: Install dependencies - name: Install dependencies
run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl gd
continue-on-error: true continue-on-error: true
- name: Install Python dependencies - name: Install Python dependencies

View file

@ -26,7 +26,7 @@ jobs:
run: sudo apt-get update run: sudo apt-get update
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |
@ -56,7 +56,7 @@ jobs:
run: sudo apt-get update run: sudo apt-get update
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |
@ -87,7 +87,7 @@ jobs:
run: sudo apt-get update run: sudo apt-get update
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |

View file

@ -119,6 +119,7 @@ jobs:
python3-dev python3-dev
libpython3-all-dev libpython3-all-dev
libssl-dev libssl-dev
libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |

1
.gitignore vendored
View file

@ -80,6 +80,7 @@ tools/jtag_openocd/openocd_configuration
tools/mfd_aes_brute/mfd_aes_brute tools/mfd_aes_brute/mfd_aes_brute
tools/mfd_aes_brute/mfd_multi_brute tools/mfd_aes_brute/mfd_multi_brute
tools/mfd_aes_brute/brute_key tools/mfd_aes_brute/brute_key
!tools/lena.bmp
fpga/__build* fpga/__build*

View file

@ -8,6 +8,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added troubleshooting entry - ARM architecture error (@francis2054) - Added troubleshooting entry - ARM architecture error (@francis2054)
- Fixed `lf pyramid sim` - wrong parameter handling (@iceman1001) - Fixed `lf pyramid sim` - wrong parameter handling (@iceman1001)
- Fixed bootloader - Ignore jitters when pressing the button (@wh201906) - Fixed bootloader - Ignore jitters when pressing the button (@wh201906)
- Changed `hf waveshare` - image loading and processing is now done using [GDlib](https://github.com/libgd/libgd) (@socram8888)
## [Steamboat Willie.4.17768][2024-01-03] ## [Steamboat Willie.4.17768][2024-01-03]
- Changed `mem spiffs dump -t` - now supports downloading direct into trace buffer (@hazardousvoltage) - Changed `mem spiffs dump -t` - now supports downloading direct into trace buffer (@hazardousvoltage)

View file

@ -97,11 +97,12 @@ if (CMAKE_TOOLCHAIN_FILE)
set(EMBED_READLINE ON) set(EMBED_READLINE ON)
set(EMBED_BZIP2 ON) set(EMBED_BZIP2 ON)
set(EMBED_LZ4 ON) set(EMBED_LZ4 ON)
set(EMBED_GD ON)
endif (CMAKE_TOOLCHAIN_FILE) endif (CMAKE_TOOLCHAIN_FILE)
if (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4) if (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD)
include(ExternalProject) include(ExternalProject)
endif (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4) endif (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD)
if (NOT SKIPREADLINE EQUAL 1) if (NOT SKIPREADLINE EQUAL 1)
if (APPLE) if (APPLE)
@ -213,6 +214,27 @@ if (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES)
set(LZ4_FOUND ON) set(LZ4_FOUND ON)
endif (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES) endif (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES)
if (NOT SKIPGD EQUAL 1)
if (EMBED_GD)
cmake_policy(SET CMP0114 NEW)
set(GD_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/gd)
# Specify SOURCE_DIR will cause some errors
ExternalProject_Add(gd
URL https://github.com/libgd/libgd/releases/download/gd-2.3.3/libgd-2.3.3.tar.gz
URL_HASH SHA256=dd3f1f0bb016edcc0b2d082e8229c822ad1d02223511997c80461481759b1ed2
PREFIX deps/gd
CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DENABLE_CPP=0 -DCMAKE_INSTALL_PREFIX=.
PATCH_COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/deps/gd-static.patch | patch -p1
)
ExternalProject_Add_StepTargets(gd configure build install)
set(GD_INCLUDE_DIRS ${GD_BUILD_DIR}/src/gd-build/include)
set(GD_LIBRARIES ${GD_BUILD_DIR}/src/gd-build/lib/libgd.a)
set(GD_FOUND ON)
else (EMBED_GD)
pkg_search_module(GD QUIET gdlib)
endif (EMBED_GD)
endif (NOT SKIPGD EQUAL 1)
if (NOT SKIPWHEREAMISYSTEM EQUAL 1) if (NOT SKIPWHEREAMISYSTEM EQUAL 1)
find_path(WHEREAMI_INCLUDE_DIRS whereami.h) find_path(WHEREAMI_INCLUDE_DIRS whereami.h)
find_library(WHEREAMI_LIBRARIES whereami) find_library(WHEREAMI_LIBRARIES whereami)
@ -332,7 +354,6 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c
${PM3_ROOT}/client/src/cmdhftopaz.c ${PM3_ROOT}/client/src/cmdhftopaz.c
${PM3_ROOT}/client/src/cmdhfvas.c ${PM3_ROOT}/client/src/cmdhfvas.c
${PM3_ROOT}/client/src/cmdhfwaveshare.c
${PM3_ROOT}/client/src/cmdhfxerox.c ${PM3_ROOT}/client/src/cmdhfxerox.c
${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdhw.c
${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlf.c
@ -495,6 +516,17 @@ if (LZ4_FOUND)
set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK}) set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK})
endif (LZ4_FOUND) endif (LZ4_FOUND)
if (NOT SKIPGD EQUAL 1 AND GD_FOUND)
set(ADDITIONAL_DIRS ${GD_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
set(ADDITIONAL_LNK ${GD_LIBRARIES} ${ADDITIONAL_LNK})
set(ADDITIONAL_LNKDIRS ${GD_LIBRARY_DIRS} ${ADDITIONAL_LNKDIRS})
set(TARGET_SOURCES
${PM3_ROOT}/client/src/imgutils.c
${PM3_ROOT}/client/src/cmdhfwaveshare.c
${TARGET_SOURCES})
add_definitions("-DHAVE_GD")
endif (NOT SKIPGD EQUAL 1 AND GD_FOUND)
if (WHEREAMI_FOUND) if (WHEREAMI_FOUND)
set(ADDITIONAL_DIRS ${WHEREAMI_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) set(ADDITIONAL_DIRS ${WHEREAMI_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK}) set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK})
@ -547,6 +579,18 @@ else (LZ4_FOUND)
message(SEND_ERROR "LZ4 library: LZ4 not found") message(SEND_ERROR "LZ4 library: LZ4 not found")
endif (LZ4_FOUND) endif (LZ4_FOUND)
if (SKIPGD EQUAL 1)
message(STATUS "GD library: skipped")
elseif (GD_FOUND)
if (EMBED_GD)
message(STATUS "GD library: embedded")
else (EMBED_GD)
message(STATUS "GD library: system library found")
endif (EMBED_GD)
else (SKIPGD EQUAL 1)
message(STATUS "GD library: GD not found, disabled")
endif (SKIPGD EQUAL 1)
if (SKIPJANSSONSYSTEM EQUAL 1) if (SKIPJANSSONSYSTEM EQUAL 1)
message(STATUS "Jansson library: local library forced") message(STATUS "Jansson library: local library forced")
else (SKIPJANSSONSYSTEM EQUAL 1) else (SKIPJANSSONSYSTEM EQUAL 1)
@ -721,14 +765,14 @@ endif (NOT SKIPPTHREAD EQUAL 1)
if (NOT SKIPPYTHON EQUAL 1) if (NOT SKIPPYTHON EQUAL 1)
# OSX have a hard time compiling python3 dependency with older cmake. # OSX have a hard time compiling python3 dependency with older cmake.
if (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) if (PYTHON3EMBED_FOUND OR PYTHON3_FOUND)
if (NOT CMAKE_VERSION VERSION_LESS 3.13) if (CMAKE_VERSION VERSION_LESS 3.13)
target_link_directories(proxmark3 PRIVATE ${ADDITIONAL_LNKDIRS}) message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" )
elseif (APPLE) endif (CMAKE_VERSION VERSION_LESS 3.13)
message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" )
endif (NOT CMAKE_VERSION VERSION_LESS 3.13)
endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND)
endif (NOT SKIPPYTHON EQUAL 1) endif (NOT SKIPPYTHON EQUAL 1)
target_link_directories(proxmark3 PRIVATE ${ADDITIONAL_LNKDIRS})
install(TARGETS proxmark3 DESTINATION "bin") install(TARGETS proxmark3 DESTINATION "bin")
install(DIRECTORY cmdscripts lualibs luascripts pyscripts resources dictionaries DESTINATION "share/proxmark3") install(DIRECTORY cmdscripts lualibs luascripts pyscripts resources dictionaries DESTINATION "share/proxmark3")

View file

@ -332,6 +332,17 @@ endif
LDLIBS += $(QTLDLIBS) LDLIBS += $(QTLDLIBS)
CXXINCLUDES += $(QTINCLUDES) CXXINCLUDES += $(QTINCLUDES)
## GD (optional)
ifneq ($(SKIPGD),1)
GDINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags gdlib 2>/dev/null)
GDLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs gdlib 2>/dev/null)
ifneq ($(GDLDLIBS),)
LDLIBS += $(GDLDLIBS)
PM3INCLUDES += $(GDINCLUDES)
GD_FOUND = 1
endif
endif
## Readline ## Readline
ifneq ($(SKIPREADLINE),1) ifneq ($(SKIPREADLINE),1)
ifeq ($(USE_BREW),1) ifeq ($(USE_BREW),1)
@ -410,6 +421,10 @@ ifeq ($(PYTHON_FOUND),1)
PM3CFLAGS += -DHAVE_PYTHON PM3CFLAGS += -DHAVE_PYTHON
endif endif
ifeq ($(GD_FOUND),1)
PM3CFLAGS += -DHAVE_GD
endif
ifeq ($(SWIG_LUA_FOUND),1) ifeq ($(SWIG_LUA_FOUND),1)
PM3CFLAGS += -DHAVE_LUA_SWIG PM3CFLAGS += -DHAVE_LUA_SWIG
endif endif
@ -510,6 +525,16 @@ else
endif endif
endif endif
ifeq ($(SKIPGD),1)
$(info GD library: skipped)
else
ifeq ($(GD_FOUND),1)
$(info GD library: GD v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion gdlib) found, enabled)
else
$(info GD library: GD not found, disabled)
endif
endif
ifeq ($(SKIPREADLINE),1) ifeq ($(SKIPREADLINE),1)
$(info Readline library: skipped) $(info Readline library: skipped)
else else
@ -605,7 +630,6 @@ SRCS = mifare/aiddesfire.c \
cmdhftopaz.c \ cmdhftopaz.c \
cmdhftexkom.c \ cmdhftexkom.c \
cmdhfvas.c \ cmdhfvas.c \
cmdhfwaveshare.c \
cmdhfxerox.c \ cmdhfxerox.c \
cmdhw.c \ cmdhw.c \
cmdlf.c \ cmdlf.c \
@ -738,6 +762,12 @@ SRCS += bucketsort.c \
lfdemod.c \ lfdemod.c \
util_posix.c util_posix.c
ifeq ($(GD_FOUND),1)
# electronic shelf labels
SRCS += imgutils.c \
cmdhfwaveshare.c
endif
# swig # swig
SWIGSRCS = SWIGSRCS =

View file

@ -0,0 +1,11 @@
--- a/src/gd.h 2024-01-07 16:51:43.749223000 +0100
+++ b/src/gd.h 2024-01-07 16:52:34.162291600 +0100
@@ -45,7 +45,7 @@
the gd sources in a project. */
/* http://gcc.gnu.org/wiki/Visibility */
-#if defined(_WIN32) || defined(CYGWIN) || defined(_WIN32_WCE)
+#if 0 // Disable DLL annotations when building statically. Needed for embedding under MinGW.
# ifdef BGDWIN32
# ifdef NONDLL
# define BGD_EXPORT_DATA_PROT

View file

@ -98,11 +98,12 @@ if (CMAKE_TOOLCHAIN_FILE)
set(EMBED_READLINE ON) set(EMBED_READLINE ON)
set(EMBED_BZIP2 ON) set(EMBED_BZIP2 ON)
set(EMBED_LZ4 ON) set(EMBED_LZ4 ON)
set(EMBED_GD ON)
endif (CMAKE_TOOLCHAIN_FILE) endif (CMAKE_TOOLCHAIN_FILE)
if (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4) if (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD)
include(ExternalProject) include(ExternalProject)
endif (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4) endif (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD)
if (NOT SKIPREADLINE EQUAL 1) if (NOT SKIPREADLINE EQUAL 1)
if (APPLE) if (APPLE)
@ -214,6 +215,27 @@ if (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES)
set(LZ4_FOUND ON) set(LZ4_FOUND ON)
endif (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES) endif (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES)
if (NOT SKIPGD EQUAL 1)
if (EMBED_GD)
cmake_policy(SET CMP0114 NEW)
set(GD_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/gd)
# Specify SOURCE_DIR will cause some errors
ExternalProject_Add(gd
URL https://github.com/libgd/libgd/releases/download/gd-2.3.3/libgd-2.3.3.tar.gz
URL_HASH SHA256=dd3f1f0bb016edcc0b2d082e8229c822ad1d02223511997c80461481759b1ed2
PREFIX deps/gd
CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DENABLE_CPP=0 -DCMAKE_INSTALL_PREFIX=.
PATCH_COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/deps/gd-static.patch | patch -p1
)
ExternalProject_Add_StepTargets(gd configure build install)
set(GD_INCLUDE_DIRS ${GD_BUILD_DIR}/src/gd-build/include)
set(GD_LIBRARIES ${GD_BUILD_DIR}/src/gd-build/lib/libgd.a)
set(GD_FOUND ON)
else (EMBED_GD)
pkg_search_module(GD QUIET gdlib)
endif (EMBED_GD)
endif (NOT SKIPGD EQUAL 1)
if (NOT SKIPWHEREAMISYSTEM EQUAL 1) if (NOT SKIPWHEREAMISYSTEM EQUAL 1)
find_path(WHEREAMI_INCLUDE_DIRS whereami.h) find_path(WHEREAMI_INCLUDE_DIRS whereami.h)
find_library(WHEREAMI_LIBRARIES whereami) find_library(WHEREAMI_LIBRARIES whereami)
@ -333,7 +355,6 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c
${PM3_ROOT}/client/src/cmdhftopaz.c ${PM3_ROOT}/client/src/cmdhftopaz.c
${PM3_ROOT}/client/src/cmdhfvas.c ${PM3_ROOT}/client/src/cmdhfvas.c
${PM3_ROOT}/client/src/cmdhfwaveshare.c
${PM3_ROOT}/client/src/cmdhfxerox.c ${PM3_ROOT}/client/src/cmdhfxerox.c
${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdhw.c
${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlf.c
@ -496,6 +517,17 @@ if (LZ4_FOUND)
set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK}) set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK})
endif (LZ4_FOUND) endif (LZ4_FOUND)
if (NOT SKIPGD EQUAL 1 AND GD_FOUND)
set(ADDITIONAL_DIRS ${GD_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
set(ADDITIONAL_LNK ${GD_LIBRARIES} ${ADDITIONAL_LNK})
set(ADDITIONAL_LNKDIRS ${GD_LIBRARY_DIRS} ${ADDITIONAL_LNKDIRS})
set(TARGET_SOURCES
${PM3_ROOT}/client/src/imgutils.c
${PM3_ROOT}/client/src/cmdhfwaveshare.c
${TARGET_SOURCES})
add_definitions("-DHAVE_GD")
endif (NOT SKIPGD EQUAL 1 AND GD_FOUND)
if (WHEREAMI_FOUND) if (WHEREAMI_FOUND)
set(ADDITIONAL_DIRS ${WHEREAMI_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) set(ADDITIONAL_DIRS ${WHEREAMI_INCLUDE_DIRS} ${ADDITIONAL_DIRS})
set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK}) set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK})
@ -548,6 +580,18 @@ else (LZ4_FOUND)
message(SEND_ERROR "LZ4 library: LZ4 not found") message(SEND_ERROR "LZ4 library: LZ4 not found")
endif (LZ4_FOUND) endif (LZ4_FOUND)
if (SKIPGD EQUAL 1)
message(STATUS "GD library: skipped")
elseif (GD_FOUND)
if (EMBED_GD)
message(STATUS "GD library: embedded")
else (EMBED_GD)
message(STATUS "GD library: system library found")
endif (EMBED_GD)
else (SKIPGD EQUAL 1)
message(STATUS "GD library: GD not found, disabled")
endif (SKIPGD EQUAL 1)
if (SKIPJANSSONSYSTEM EQUAL 1) if (SKIPJANSSONSYSTEM EQUAL 1)
message(STATUS "Jansson library: local library forced") message(STATUS "Jansson library: local library forced")
else (SKIPJANSSONSYSTEM EQUAL 1) else (SKIPJANSSONSYSTEM EQUAL 1)
@ -722,10 +766,10 @@ endif (NOT SKIPPTHREAD EQUAL 1)
if (NOT SKIPPYTHON EQUAL 1) if (NOT SKIPPYTHON EQUAL 1)
# OSX have a hard time compiling python3 dependency with older cmake. # OSX have a hard time compiling python3 dependency with older cmake.
if (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) if (PYTHON3EMBED_FOUND OR PYTHON3_FOUND)
if (NOT CMAKE_VERSION VERSION_LESS 3.13) if (CMAKE_VERSION VERSION_LESS 3.13)
target_link_directories(pm3rrg_rdv4 PRIVATE ${ADDITIONAL_LNKDIRS}) message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" )
elseif (APPLE) endif (CMAKE_VERSION VERSION_LESS 3.13)
message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" )
endif (NOT CMAKE_VERSION VERSION_LESS 3.13)
endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND)
endif (NOT SKIPPYTHON EQUAL 1) endif (NOT SKIPPYTHON EQUAL 1)
target_link_directories(pm3rrg_rdv4 PRIVATE ${ADDITIONAL_LNKDIRS})

View file

@ -566,7 +566,9 @@ static command_t CommandTable[] = {
{"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"},
{"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"},
{"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service }"}, {"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service }"},
#ifdef HAVE_GD
{"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"},
#endif
{"xerox", CmdHFXerox, AlwaysAvailable, "{ Fuji/Xerox cartridge RFIDs... }"}, {"xerox", CmdHFXerox, AlwaysAvailable, "{ Fuji/Xerox cartridge RFIDs... }"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"},
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},

View file

@ -28,31 +28,7 @@
#include "fileutils.h" #include "fileutils.h"
#include "util_posix.h" // msleep #include "util_posix.h" // msleep
#include "cliparser.h" #include "cliparser.h"
#include "imgutils.h"
// Currently the largest pixel 880*528 only needs 58.08K bytes
#define WSMAPSIZE 60000
typedef struct {
uint8_t B;
uint8_t M;
uint32_t fsize;
uint16_t res1;
uint16_t res2;
uint32_t offset;
uint32_t Bit_Pixel;
uint32_t BMP_Width;
uint32_t BMP_Height;
uint16_t planes;
uint16_t bpp;
uint32_t ctype;
uint32_t dsize;
uint32_t hppm;
uint32_t vppm;
uint32_t colorsused;
uint32_t colorreq;
uint32_t Color_1; //Color palette
uint32_t Color_2;
} PACKED bmp_header_t;
#define EPD_1IN54B 0 #define EPD_1IN54B 0
#define EPD_1IN54C 1 #define EPD_1IN54C 1
@ -105,254 +81,24 @@ static model_t models[] = {
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize, const uint8_t model_nr) { static uint8_t * map8to1(gdImagePtr img, int color) {
if (bmpsize < sizeof(bmp_header_t)) { // Calculate width rounding up
return PM3_ESOFT; uint16_t width8 = (gdImageSX(img) + 7) / 8;
uint8_t * colormap8 = malloc(width8 * gdImageSY(img));
if (!colormap8) {
return NULL;
} }
bmp_header_t *pbmpheader = (bmp_header_t *)bmp;
PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused);
PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp);
if ((pbmpheader->BMP_Width != models[model_nr].width) || (pbmpheader->BMP_Height != models[model_nr].height)) {
PrintAndLogEx(WARNING, "Invalid BMP size, expected %ix%i, got %ix%i", models[model_nr].width, models[model_nr].height, pbmpheader->BMP_Width, pbmpheader->BMP_Height);
}
return pbmpheader->bpp;
}
static int read_bmp_bitmap(const uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red) {
bmp_header_t *pbmpheader = (bmp_header_t *)bmp;
// check file is bitmap
if (pbmpheader->bpp != 1) {
return PM3_ESOFT;
}
if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42
PrintAndLogEx(WARNING, "The file is not a BMP!");
return PM3_ESOFT;
}
PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize);
PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset);
if (pbmpheader->fsize > bmpsize) {
PrintAndLogEx(WARNING, "The file is truncated!");
return PM3_ESOFT;
}
uint8_t color_flag = pbmpheader->Color_1;
// Get BMP file data pointer
uint32_t offset = pbmpheader->offset;
uint16_t width = pbmpheader->BMP_Width;
uint16_t height = pbmpheader->BMP_Height;
if ((width + 8) * height > WSMAPSIZE * 8) {
PrintAndLogEx(WARNING, "The file is too large, aborting!");
return PM3_ESOFT;
}
uint16_t X, Y;
uint16_t Image_Width_Byte = (width % 8 == 0) ? (width / 8) : (width / 8 + 1);
uint16_t Bmp_Width_Byte = (Image_Width_Byte % 4 == 0) ? Image_Width_Byte : ((Image_Width_Byte / 4 + 1) * 4);
*black = calloc(WSMAPSIZE, sizeof(uint8_t));
if (*black == NULL) {
return PM3_EMALLOC;
}
// Write data into RAM
for (Y = 0; Y < height; Y++) { // columns
for (X = 0; X < Bmp_Width_Byte; X++) { // lines
if ((X < Image_Width_Byte) && ((X + (height - Y - 1) * Image_Width_Byte) < WSMAPSIZE)) {
(*black)[X + (height - Y - 1) * Image_Width_Byte] = color_flag ? bmp[offset] : ~bmp[offset];
}
offset++;
}
}
if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
// for BW+Red screens:
*red = calloc(WSMAPSIZE, sizeof(uint8_t));
if (*red == NULL) {
free(*black);
return PM3_EMALLOC;
}
}
return PM3_SUCCESS;
}
static void rgb_to_gray(const int16_t *chanR, const int16_t *chanG, const int16_t *chanB,
uint16_t width, uint16_t height, int16_t *chanGrey) {
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
// greyscale conversion
float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width];
// Csrgb = 12.92 Clinear when Clinear <= 0.0031308
// Csrgb = 1.055 Clinear1/2.4 - 0.055 when Clinear > 0.0031308
chanGrey[X + Y * width] = Clinear;
}
}
}
// Floyd-Steinberg dithering
static void dither_chan_inplace(int16_t *chan, uint16_t width, uint16_t height) {
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
int16_t oldp = chan[X + Y * width];
int16_t newp = oldp > 127 ? 255 : 0;
chan[X + Y * width] = newp;
int16_t err = oldp - newp;
const float m[] = {7, 3, 5, 1};
if (X < width - 1) {
chan[X + 1 + Y * width] = chan[X + 1 + Y * width] + m[0] / 16 * err;
}
if (Y < height - 1) {
chan[X - 1 + (Y + 1) * width] = chan[X - 1 + (Y + 1) * width] + m[1] / 16 * err;
chan[X + (Y + 1) * width] = chan[X + (Y + 1) * width] + m[2] / 16 * err;
}
if ((X < width - 1) && (Y < height - 1)) {
chan[X + 1 + (Y + 1) * width] = chan[X + 1 + (Y + 1) * width] + m[3] / 16 * err;
}
}
}
}
static uint32_t color_compare(int16_t r1, int16_t g1, int16_t b1, int16_t r2, int16_t g2, int16_t b2) {
// Compute (square of) distance from oldR/G/B to this color
int16_t inR = r1 - r2;
int16_t inG = g1 - g2;
int16_t inB = b1 - b2;
// use RGB-to-grey weighting
float dist = 0.2126 * inR * inR + 0.7152 * inG * inG + 0.0722 * inB * inB;
return dist;
}
static void nearest_color(int16_t oldR, int16_t oldG, int16_t oldB, const uint8_t *palette,
uint16_t palettelen, uint8_t *newR, uint8_t *newG, uint8_t *newB) {
uint32_t bestdist = 0x7FFFFFFF;
for (uint16_t i = 0; i < palettelen; i++) {
uint8_t R = palette[i * 3 + 0];
uint8_t G = palette[i * 3 + 1];
uint8_t B = palette[i * 3 + 2];
uint32_t dist = color_compare(oldR, oldG, oldB, R, G, B);
if (dist < bestdist) {
bestdist = dist;
*newR = R;
*newG = G;
*newB = B;
}
}
}
static void dither_rgb_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, uint8_t *palette, uint16_t palettelen) {
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
// scan odd lines in the opposite direction
uint16_t XX = X;
if (Y % 2) {
XX = width - X - 1;
}
int16_t oldR = chanR[XX + Y * width];
int16_t oldG = chanG[XX + Y * width];
int16_t oldB = chanB[XX + Y * width];
uint8_t newR = 0, newG = 0, newB = 0;
nearest_color(oldR, oldG, oldB, palette, palettelen, &newR, &newG, &newB);
chanR[XX + Y * width] = newR;
chanG[XX + Y * width] = newG;
chanB[XX + Y * width] = newB;
int16_t errR = oldR - newR;
int16_t errG = oldG - newG;
int16_t errB = oldB - newB;
const float m[] = {7, 3, 5, 1};
if (Y % 2) {
if (XX > 0) {
chanR[XX - 1 + Y * width] = (chanR[XX - 1 + Y * width] + m[0] / 16 * errR);
chanG[XX - 1 + Y * width] = (chanG[XX - 1 + Y * width] + m[0] / 16 * errG);
chanB[XX - 1 + Y * width] = (chanB[XX - 1 + Y * width] + m[0] / 16 * errB);
}
if (Y < height - 1) {
chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[3] / 16 * errR);
chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[3] / 16 * errG);
chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[3] / 16 * errB);
chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR);
chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG);
chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB);
}
if ((XX < width - 1) && (Y < height - 1)) {
chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[1] / 16 * errR);
chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[1] / 16 * errG);
chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[1] / 16 * errB);
}
} else {
if (XX < width - 1) {
chanR[XX + 1 + Y * width] = (chanR[XX + 1 + Y * width] + m[0] / 16 * errR);
chanG[XX + 1 + Y * width] = (chanG[XX + 1 + Y * width] + m[0] / 16 * errG);
chanB[XX + 1 + Y * width] = (chanB[XX + 1 + Y * width] + m[0] / 16 * errB);
}
if (Y < height - 1) {
chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[1] / 16 * errR);
chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[1] / 16 * errG);
chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[1] / 16 * errB);
chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR);
chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG);
chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB);
}
if ((XX < width - 1) && (Y < height - 1)) {
chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[3] / 16 * errR);
chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[3] / 16 * errG);
chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[3] / 16 * errB);
}
}
}
}
}
static void rgb_to_gray_red_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height) {
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width];
if ((chanR[X + Y * width] < chanG[X + Y * width] && chanR[X + Y * width] < chanB[X + Y * width])) {
chanR[X + Y * width] = Clinear;
chanG[X + Y * width] = Clinear;
chanB[X + Y * width] = Clinear;
}
}
}
}
static void threshold_chan(const int16_t *colorchan, uint16_t width, uint16_t height, uint8_t threshold, uint8_t *colormap) {
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
colormap[X + Y * width] = colorchan[X + Y * width] < threshold;
}
}
}
static void threshold_rgb_black_red(const int16_t *chanR, const int16_t *chanG, const int16_t *chanB,
uint16_t width, uint16_t height, uint8_t threshold_black,
uint8_t threshold_red, uint8_t *blackmap, uint8_t *redmap) {
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
if ((chanR[X + Y * width] < threshold_black) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) {
blackmap[X + Y * width] = 1;
redmap[X + Y * width] = 0;
} else if ((chanR[X + Y * width] > threshold_red) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) {
blackmap[X + Y * width] = 0;
redmap[X + Y * width] = 1;
} else {
blackmap[X + Y * width] = 0;
redmap[X + Y * width] = 0;
}
}
}
}
static void map8to1(const uint8_t *colormap, uint16_t width, uint16_t height, uint8_t *colormap8) {
uint16_t width8;
if (width % 8 == 0) {
width8 = width / 8;
} else {
width8 = width / 8 + 1;
}
uint8_t data = 0; uint8_t data = 0;
uint8_t count = 0; uint8_t count = 0;
for (uint16_t Y = 0; Y < height; Y++) { for (uint16_t Y = 0; Y < gdImageSY(img); Y++) {
for (uint16_t X = 0; X < width; X++) { for (uint16_t X = 0; X < gdImageSX(img); X++) {
data = data | colormap[X + Y * width]; if (gdImageGetPixel(img, X, Y) == color) {
data |= 1;
}
count += 1; count += 1;
if ((count >= 8) || (X == width - 1)) { if ((count >= 8) || (X == gdImageSX(img) - 1)) {
colormap8[X / 8 + Y * width8] = (~data) & 0xFF; colormap8[X / 8 + Y * width8] = (~data) & 0xFF;
count = 0; count = 0;
data = 0; data = 0;
@ -360,194 +106,8 @@ static void map8to1(const uint8_t *colormap, uint16_t width, uint16_t height, ui
data = (data << 1) & 0xFF; data = (data << 1) & 0xFF;
} }
} }
}
static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red, char *filename, bool save_conversions) { return colormap8;
bmp_header_t *pbmpheader = (bmp_header_t *)bmp;
// check file is full color
if ((pbmpheader->bpp != 24) && (pbmpheader->bpp != 32)) {
return PM3_ESOFT;
}
if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42
PrintAndLogEx(WARNING, "The file is not a BMP!");
return PM3_ESOFT;
}
PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize);
PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset);
if (pbmpheader->fsize > bmpsize) {
PrintAndLogEx(WARNING, "The file is truncated!");
return PM3_ESOFT;
}
// Get BMP file data pointer
uint32_t offset = pbmpheader->offset;
uint16_t width = pbmpheader->BMP_Width;
uint16_t height = pbmpheader->BMP_Height;
if ((width + 8) * height > WSMAPSIZE * 8) {
PrintAndLogEx(WARNING, "The file is too large, aborting!");
return PM3_ESOFT;
}
int16_t *chanR = calloc(((size_t)width) * height, sizeof(int16_t));
if (chanR == NULL) {
return PM3_EMALLOC;
}
int16_t *chanG = calloc(((size_t)width) * height, sizeof(int16_t));
if (chanG == NULL) {
free(chanR);
return PM3_EMALLOC;
}
int16_t *chanB = calloc(((size_t)width) * height, sizeof(int16_t));
if (chanB == NULL) {
free(chanR);
free(chanG);
return PM3_EMALLOC;
}
// Extracting BMP chans
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
chanB[X + (height - Y - 1) * width] = bmp[offset++];
chanG[X + (height - Y - 1) * width] = bmp[offset++];
chanR[X + (height - Y - 1) * width] = bmp[offset++];
if (pbmpheader->bpp == 32) // Skip Alpha chan
offset++;
}
// Skip line padding
offset += width % 4;
}
if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
// for BW+Red screens:
uint8_t *mapBlack = calloc(((size_t)width) * height, sizeof(uint8_t));
if (mapBlack == NULL) {
free(chanR);
free(chanG);
free(chanB);
return PM3_EMALLOC;
}
uint8_t *mapRed = calloc(((size_t)width) * height, sizeof(uint8_t));
if (mapRed == NULL) {
free(chanR);
free(chanG);
free(chanB);
free(mapBlack);
return PM3_EMALLOC;
}
rgb_to_gray_red_inplace(chanR, chanG, chanB, width, height);
uint8_t palette[] = {0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; // black, white, red
dither_rgb_inplace(chanR, chanG, chanB, width, height, palette, sizeof(palette) / 3);
threshold_rgb_black_red(chanR, chanG, chanB, width, height, 128, 128, mapBlack, mapRed);
if (save_conversions) {
// fill BMP chans
offset = pbmpheader->offset;
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
bmp[offset++] = chanB[X + (height - Y - 1) * width] & 0xFF;
bmp[offset++] = chanG[X + (height - Y - 1) * width] & 0xFF;
bmp[offset++] = chanR[X + (height - Y - 1) * width] & 0xFF;
if (pbmpheader->bpp == 32) // Fill Alpha chan
bmp[offset++] = 0xFF;
}
// Skip line padding
offset += width % 4;
}
PrintAndLogEx(INFO, "Saving red+black dithered version...");
if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename);
free(chanR);
free(chanG);
free(chanB);
free(mapBlack);
free(mapRed);
return PM3_EIO;
}
}
free(chanR);
free(chanG);
free(chanB);
*black = calloc(WSMAPSIZE, sizeof(uint8_t));
if (*black == NULL) {
free(mapBlack);
free(mapRed);
return PM3_EMALLOC;
}
map8to1(mapBlack, width, height, *black);
free(mapBlack);
*red = calloc(WSMAPSIZE, sizeof(uint8_t));
if (*red == NULL) {
free(mapRed);
free(*black);
return PM3_EMALLOC;
}
map8to1(mapRed, width, height, *red);
free(mapRed);
} else {
// for BW-only screens:
int16_t *chanGrey = calloc(((size_t)width) * height, sizeof(int16_t));
if (chanGrey == NULL) {
free(chanR);
free(chanG);
free(chanB);
return PM3_EMALLOC;
}
rgb_to_gray(chanR, chanG, chanB, width, height, chanGrey);
dither_chan_inplace(chanGrey, width, height);
uint8_t *mapBlack = calloc(((size_t)width) * height, sizeof(uint8_t));
if (mapBlack == NULL) {
free(chanR);
free(chanG);
free(chanB);
free(chanGrey);
return PM3_EMALLOC;
}
threshold_chan(chanGrey, width, height, 128, mapBlack);
if (save_conversions) {
// fill BMP chans
offset = pbmpheader->offset;
for (uint16_t Y = 0; Y < height; Y++) {
for (uint16_t X = 0; X < width; X++) {
bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
if (pbmpheader->bpp == 32) // Fill Alpha chan
bmp[offset++] = 0xFF;
}
// Skip line padding
offset += width % 4;
}
PrintAndLogEx(INFO, "Saving black dithered version...");
if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename);
free(chanGrey);
free(chanR);
free(chanG);
free(chanB);
free(mapBlack);
return PM3_EIO;
}
}
free(chanGrey);
free(chanR);
free(chanG);
free(chanB);
*black = calloc(WSMAPSIZE, sizeof(uint8_t));
if (*black == NULL) {
free(mapBlack);
return PM3_EMALLOC;
}
map8to1(mapBlack, width, height, *black);
free(mapBlack);
}
return PM3_SUCCESS;
} }
static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, const uint8_t *black) { static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, const uint8_t *black) {
@ -1003,13 +563,13 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdHF14AWSLoadBmp(const char *Cmd) { static int CmdHF14AWSLoad(const char *Cmd) {
char desc[800] = {0}; char desc[800] = {0};
for (uint8_t i = 0; i < MEND; i++) { for (uint8_t i = 0; i < MEND; i++) {
snprintf(desc + strlen(desc), snprintf(desc + strlen(desc),
sizeof(desc) - strlen(desc), sizeof(desc) - strlen(desc),
"hf waveshare loadbmp -f myfile -m %2u -> %s ( %u, %u )\n", "hf waveshare load -f myfile -m %2u -> %s ( %u, %u )\n",
i, i,
models[i].desc, models[i].desc,
models[i].width, models[i].width,
@ -1018,8 +578,8 @@ static int CmdHF14AWSLoadBmp(const char *Cmd) {
} }
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf waveshare loadbmp", CLIParserInit(&ctx, "hf waveshare load",
"Load BMP file to Waveshare NFC ePaper.", "Load image file to Waveshare NFC ePaper",
desc desc
); );
@ -1029,24 +589,25 @@ static int CmdHF14AWSLoadBmp(const char *Cmd) {
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_int1("m", NULL, "<nr>", modeldesc), arg_int1("m", NULL, "<nr>", modeldesc),
arg_lit0("s", "save", "save dithered version in filename-[n].bmp, only for RGB BMP"), arg_str1("f", "file", "<fn>", "specify image to upload to tag"),
arg_str1("f", "file", "<fn>", "specify filename[.bmp] to upload to tag"), arg_str0("s", "save", "<fn>", "save paletized version in file"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
int model_nr = arg_get_int_def(ctx, 1, -1); int model_nr = arg_get_int_def(ctx, 1, -1);
bool save_conversions = arg_get_lit(ctx, 2);
int fnlen = 0; int infilelen, outfilelen;
char filename[FILE_PATH_SIZE] = {0}; char infile[FILE_PATH_SIZE];
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); char outfile[FILE_PATH_SIZE];
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)infile, FILE_PATH_SIZE, &infilelen);
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)outfile, FILE_PATH_SIZE, &outfilelen);
CLIParserFree(ctx); CLIParserFree(ctx);
//Validations //Validations
if (fnlen < 1) { if (infilelen < 1) {
PrintAndLogEx(WARNING, "Missing filename"); PrintAndLogEx(WARNING, "Missing input file");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (model_nr == -1) { if (model_nr == -1) {
@ -1057,64 +618,85 @@ static int CmdHF14AWSLoadBmp(const char *Cmd) {
PrintAndLogEx(WARNING, "Unknown model"); PrintAndLogEx(WARNING, "Unknown model");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (!g_session.pm3_present && !outfilelen) {
PrintAndLogEx(WARNING, "Offline - can only perform image conversion");
return PM3_ENOTTY;
}
uint8_t *bmp = NULL; bool model_has_red = model_nr == M1in54B || model_nr == M2in13B;
uint8_t *black = NULL;
uint8_t *red = NULL; gdImagePtr rgb_img = gdImageCreateFromFile(infile);
size_t bytes_read = 0; if (!rgb_img) {
if (loadFile_safe(filename, ".bmp", (void **)&bmp, &bytes_read) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Could not load image from " _YELLOW_("%s"), infile);
PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename);
return PM3_EFILE; return PM3_EFILE;
} }
if (bmp == NULL) {
if (
gdImageSX(rgb_img) != models[model_nr].width ||
gdImageSY(rgb_img) != models[model_nr].height
) {
PrintAndLogEx(WARNING, "Image size does not match panel size");
gdImageDestroy(rgb_img);
return PM3_EFILE;
}
int pal_len = 2;
int pal[3];
pal[0] = gdTrueColorAlpha(0xFF, 0xFF, 0xFF, 0); // White
pal[1] = gdTrueColorAlpha(0x00, 0x00, 0x00, 0); // Black
if (model_has_red) {
pal_len = 3;
pal[2] = gdTrueColorAlpha(0xFF, 0x00, 0x00, 0); // Red
}
gdImagePtr pal_img = img_palettize(rgb_img, pal, pal_len);
gdImageDestroy(rgb_img);
if (!pal_img) {
PrintAndLogEx(WARNING, "Could not convert image");
return PM3_EMALLOC; return PM3_EMALLOC;
} }
if (bytes_read < sizeof(bmp_header_t)) {
free(bmp); if (outfilelen) {
return PM3_ESOFT; if (gdImageFile(pal_img, outfile)) {
PrintAndLogEx(INFO, "Save converted image to " _YELLOW_("%s"), outfile);
gdImageDestroy(pal_img);
return PM3_SUCCESS;
} else {
PrintAndLogEx(WARNING, "Could not save converted image", outfile);
gdImageDestroy(pal_img);
return PM3_EFILE;
}
} }
int depth = picture_bit_depth(bmp, bytes_read, model_nr); uint8_t * black_plane = map8to1(pal_img, 1);
if (depth == PM3_ESOFT) { if (!black_plane) {
PrintAndLogEx(ERR, "Error, BMP file is too small"); PrintAndLogEx(WARNING, "Could not convert image to bit plane");
free(bmp); gdImageDestroy(pal_img);
return PM3_ESOFT; return PM3_EMALLOC;
} else if (depth == 1) {
PrintAndLogEx(DEBUG, "BMP file is a bitmap");
if (read_bmp_bitmap(bmp, bytes_read, model_nr, &black, &red) != PM3_SUCCESS) {
free(bmp);
return PM3_ESOFT;
}
} else if (depth == 24) {
PrintAndLogEx(DEBUG, "BMP file is a RGB");
if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) {
free(bmp);
return PM3_ESOFT;
}
} else if (depth == 32) {
PrintAndLogEx(DEBUG, "BMP file is a RGBA, we will ignore the Alpha channel");
if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) {
free(bmp);
return PM3_ESOFT;
}
} else {
PrintAndLogEx(ERR, "Error, BMP color depth %i not supported. Must be 1 (BW), 24 (RGB) or 32 (RGBA)", depth);
free(bmp);
return PM3_ESOFT;
} }
free(bmp);
start_drawing(model_nr, black, red); uint8_t * red_plane = NULL;
free(black); if (model_has_red) {
if ((model_nr == M1in54B) || (model_nr == M2in13B)) { red_plane = map8to1(pal_img, 2);
free(red); if (!red_plane) {
PrintAndLogEx(WARNING, "Could not convert image to bit plane");
free(black_plane);
gdImageDestroy(pal_img);
return PM3_EMALLOC;
}
} }
return PM3_SUCCESS; gdImageDestroy(pal_img);
int res = start_drawing(model_nr, black_plane, red_plane);
free(black_plane);
free(red_plane);
return res;
} }
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"loadbmp", CmdHF14AWSLoadBmp, IfPm3Iso14443a, "Load BMP file to Waveshare NFC ePaper"}, {"load", CmdHF14AWSLoad, AlwaysAvailable, "Load image file to Waveshare NFC ePaper"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

159
client/src/imgutils.c Normal file
View file

@ -0,0 +1,159 @@
#include <assert.h>
#include "imgutils.h"
struct ycbcr_t {
int y;
int cb;
int cr;
};
static void rgb_to_ycbcr(int rgb, struct ycbcr_t * ycbcr) {
int r = gdTrueColorGetRed(rgb);
int g = gdTrueColorGetGreen(rgb);
int b = gdTrueColorGetBlue(rgb);
/*
* Below is a fixed-point version of the following code:
* ycbcr->y = r * 0.29900 + g * 0.58700 + b * 0.11400;
* ycbcr->cb = r * -0.16874 + g * -0.33126 + b * 0.50000 + 128;
* ycbcr->cr = r * 0.50000 + g * -0.41869 + b * -0.08131 + 128;
*/
ycbcr->y = (r * 19595 + g * 38470 + b * 7471) / 65536;
ycbcr->cb = (r * -11059 + g * -21709 + b * 32768) / 65536 + 128;
ycbcr->cr = (r * 32768 + g * -27439 + b * -5329) / 65536 + 128;
}
static inline void cap_comp(int * x) {
if (*x < 0) {
*x = 0;
} else if (*x > 255) {
*x = 255;
}
}
/*
* The following function implements a Floyd-Steinberg in YCbCr color space.
*
* Using this colorspace, the Euclidean distance between colors is closer to human perception than
* in sRGB, which results in a more accurate color rendering.
*
* A comparison can be found at https://twitter.com/Socram4x8/status/1733157380097995205/photo/1.
*/
gdImagePtr img_palettize(gdImagePtr rgb, int * palette, int palette_size) {
assert(rgb != NULL);
assert(palette != NULL);
assert(palette_size >= 2 && palette_size < 256);
// Create paletized image
gdImagePtr res = gdImageCreate(gdImageSX(rgb), gdImageSY(rgb));
if (!res) {
return NULL;
}
// Allocate space for palette in YCbCr
struct ycbcr_t * pal_ycbcr = calloc(palette_size, sizeof(struct ycbcr_t));
if (!pal_ycbcr) {
gdImageDestroy(res);
return NULL;
}
/*
* Initialize the column's error array.
*
* Note that we are storing two extra values so we don't have to do boundary checking at
* the left and right edges of the image.
*
* To reduce shifts and increase accuracy, each entry is stored with 16x times the error,
* and gets divided by that amount when it is read.
*/
struct ycbcr_t * forward = calloc(gdImageSX(rgb) + 2, sizeof(struct ycbcr_t));
if (!forward) {
free(pal_ycbcr);
gdImageDestroy(res);
return NULL;
}
// Convert palette to YCbCr and allocate in image
for (int i = 0; i < palette_size; i++) {
int c = palette[i];
rgb_to_ycbcr(c, pal_ycbcr + i);
gdImageColorAllocate(res, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), gdTrueColorGetBlue(c));
}
for (int y = 0; y < gdImageSY(rgb); y++) {
// Load current row error and reset its storage
struct ycbcr_t row_err = forward[1];
forward[1].y = forward[1].cb = forward[1].cr = 0;
for (int x = 0; x < gdImageSX(rgb); x++) {
struct ycbcr_t pix;
rgb_to_ycbcr(gdImageGetTrueColorPixel(rgb, x, y), &pix);
// Add error for current pixel
pix.y += row_err.y / 16;
pix.cb += row_err.cb / 16;
pix.cr += row_err.cr / 16;
// Cap in case it went to imaginary color territory
cap_comp(&pix.y);
cap_comp(&pix.cb);
cap_comp(&pix.cr);
/*
* Iterate through all candidate colors and find the nearest one using the
* squared Euclidean distance.
*/
int best_idx = 0;
struct ycbcr_t best_err = { 0 };
int best_score = 0x7FFFFFFF;
for (int can_idx = 0; can_idx < palette_size; can_idx++) {
struct ycbcr_t can_err = {
.y = pix.y - pal_ycbcr[can_idx].y,
.cb = pix.cb - pal_ycbcr[can_idx].cb,
.cr = pix.cr - pal_ycbcr[can_idx].cr,
};
int can_score = (
can_err.y * can_err.y +
can_err.cb * can_err.cb +
can_err.cr * can_err.cr
);
if (can_score < best_score) {
best_idx = can_idx;
best_score = can_score;
best_err = can_err;
}
}
// Set current pixel
gdImageSetPixel(res, x, y, best_idx);
// Propagate error within the current row, to the pixel to the right
row_err.y = best_err.y * 7 + forward[x + 2].y;
row_err.cb = best_err.cb * 7 + forward[x + 2].cb;
row_err.cr = best_err.cr * 7 + forward[x + 2].cr;
// Add error to bottom left
forward[x + 0].y += best_err.y * 3;
forward[x + 0].cb += best_err.cb * 3;
forward[x + 0].cr += best_err.cr * 3;
// Add error to bottom center
forward[x + 1].y += best_err.y * 5;
forward[x + 1].cb += best_err.cb * 5;
forward[x + 1].cr += best_err.cr * 5;
// Set error to bottom right
forward[x + 2].y = best_err.y * 1;
forward[x + 2].cb = best_err.cb * 1;
forward[x + 2].cr = best_err.cr * 1;
}
}
free(forward);
free(pal_ycbcr);
return res;
}

25
client/src/imgutils.h Normal file
View file

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Image utilities
//-----------------------------------------------------------------------------
#ifndef IMGUTILS_H__
#define IMGUTILS_H__
#include <gd.h>
gdImagePtr img_palettize(gdImagePtr rgb, int * palette, int palette_size);
#endif

View file

@ -72,6 +72,9 @@ you can skip the installation of `qtbase5-dev`.
👉 If you don't need support for Python3 scripts in the Proxmark3 client, 👉 If you don't need support for Python3 scripts in the Proxmark3 client,
you can skip the installation of `libpython3-dev`. you can skip the installation of `libpython3-dev`.
👉 If you don't need support for NFC ePaper devices,
you can skip the installation of `libgd-dev`.
### Failed to load module... ### Failed to load module...
⚠️ If you get some (non blocking) error at runtime such as _Gtk-Message: Failed to load module "canberra-gtk-module"_ ⚠️ If you get some (non blocking) error at runtime such as _Gtk-Message: Failed to load module "canberra-gtk-module"_
you may have to install `libcanberra-gtk-module`. you may have to install `libcanberra-gtk-module`.
@ -82,7 +85,7 @@ you may have to install `libcanberra-gtk-module`.
```sh ```sh
sudo pacman -Syu git base-devel readline bzip2 lz4 arm-none-eabi-gcc \ sudo pacman -Syu git base-devel readline bzip2 lz4 arm-none-eabi-gcc \
arm-none-eabi-newlib qt5-base bluez python --needed arm-none-eabi-newlib qt5-base bluez python gd --needed
``` ```
### If you don't need... ### If you don't need...
@ -95,6 +98,9 @@ you can skip the installation of `qt5-base`.
👉 If you don't need support for Python3 scripts in the Proxmark3 client, 👉 If you don't need support for Python3 scripts in the Proxmark3 client,
you can skip the installation of `python`. you can skip the installation of `python`.
👉 If you don't need support for NFC ePaper devices,
you can skip the installation of `gd`.
## On Fedora ## On Fedora
^[Top](#top) ^[Top](#top)
@ -102,7 +108,7 @@ you can skip the installation of `python`.
```sh ```sh
sudo dnf install git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib \ sudo dnf install git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib \
readline-devel bzip2-devel lz4-devel qt5-qtbase-devel bluez-libs-devel \ readline-devel bzip2-devel lz4-devel qt5-qtbase-devel bluez-libs-devel \
python3-devel libatomic openssl-devel python3-devel libatomic openssl-devel gd-devel
``` ```
### If you don't need... ### If you don't need...
@ -115,6 +121,9 @@ you can skip the installation of `qt5-qtbase-devel`.
👉 If you don't need support for Python3 scripts in the Proxmark3 client, 👉 If you don't need support for Python3 scripts in the Proxmark3 client,
you can skip the installation of `python3-devel`. you can skip the installation of `python3-devel`.
👉 If you don't need support for NFC ePaper devices,
you can skip the installation of `gd-devel`.
## On openSUSE ## On openSUSE
^[Top](#top) ^[Top](#top)
@ -122,7 +131,8 @@ you can skip the installation of `python3-devel`.
```sh ```sh
sudo zypper install git patterns-devel-base-devel_basis gcc-c++ \ sudo zypper install git patterns-devel-base-devel_basis gcc-c++ \
readline-devel libbz2-devel liblz4-devel cross-arm-none-gcc9 \ readline-devel libbz2-devel liblz4-devel cross-arm-none-gcc9 \
cross-arm-none-newlib-devel python3-devel libqt5-qtbase-devel libopenssl-devel cross-arm-none-newlib-devel python3-devel libqt5-qtbase-devel \
libopenssl-devel gd-devel
``` ```
Note that Bluez is not available on openSUSE so the native Bluetooth support won't be available in the client. Note that Bluez is not available on openSUSE so the native Bluetooth support won't be available in the client.
@ -134,6 +144,9 @@ you can skip the installation of `libqt5-qtbase-devel`.
👉 If you don't need support for Python3 scripts in the Proxmark3 client, 👉 If you don't need support for Python3 scripts in the Proxmark3 client,
you can skip the installation of `python3-devel`. you can skip the installation of `python3-devel`.
👉 If you don't need support for NFC ePaper devices,
you can skip the installation of `gd-devel`.
# Clone the repository # Clone the repository
^[Top](#top) ^[Top](#top)

View file

@ -169,11 +169,12 @@ Install dependencies:
```sh ```sh
sudo apt-get install --no-install-recommends git ca-certificates build-essential pkg-config \ sudo apt-get install --no-install-recommends git ca-certificates build-essential pkg-config \
libreadline-dev gcc-arm-none-eabi libnewlib-dev \ libreadline-dev gcc-arm-none-eabi libnewlib-dev \
libbz2-dev liblz4-dev libpython3-dev qtbase5-dev libssl-dev libbz2-dev liblz4-dev libpython3-dev qtbase5-dev libssl-dev libgd-dev
``` ```
_note_ _note_
If you don't need the graphical components of the Proxmark3 client, you can skip the installation of `qtbase5-dev`. If you don't need the graphical components of the Proxmark3 client, you can skip the installation of `qtbase5-dev`.
If you don't need support for Python3 scripts in the Proxmark3 client, you can skip the installation of `libpython3-dev`. If you don't need support for Python3 scripts in the Proxmark3 client, you can skip the installation of `libpython3-dev`.
If you don't need support for NFC ePaper devices, you can skip the installation of `libgd-dev`.
## Clone the Iceman repository ## Clone the Iceman repository
^[Top](#top) ^[Top](#top)

View file

@ -108,12 +108,14 @@ then, install proxmark dependencies:
sudo apt-get install --no-install-recommends \ sudo apt-get install --no-install-recommends \
git ca-certificates build-essential pkg-config \ git ca-certificates build-essential pkg-config \
libreadline-dev gcc-arm-none-eabi libnewlib-dev \ libreadline-dev gcc-arm-none-eabi libnewlib-dev \
libbz2-dev liblz4-dev libpython3-dev qtbase5-dev libssl-dev libbz2-dev liblz4-dev libpython3-dev qtbase5-dev \
libssl-dev libgd-dev
``` ```
_note_ _note_
If you don't need the graphical components of the Proxmark3 client, you can skip the installation of `qtbase5-dev`. If you don't need the graphical components of the Proxmark3 client, you can skip the installation of `qtbase5-dev`.
If you don't need support for Python3 scripts in the Proxmark3 client, you can skip the installation of `libpython3-dev`. If you don't need support for Python3 scripts in the Proxmark3 client, you can skip the installation of `libpython3-dev`.
If you don't need support for NFC ePaper devices, you can skip the installation of `libgd-dev`.
## X Server Installation ## X Server Installation
^[Top](#top) ^[Top](#top)

View file

@ -161,7 +161,7 @@ These instructions will show how to setup the environment on OSX to the point wh
2. Install dependencies: 2. Install dependencies:
``` ```
brew install readline qt5 pkgconfig coreutils brew install readline qt5 gd pkgconfig coreutils
brew install RfidResearchGroup/proxmark3/arm-none-eabi-gcc brew install RfidResearchGroup/proxmark3/arm-none-eabi-gcc
``` ```
3. (optional) Install makefile dependencies: 3. (optional) Install makefile dependencies:

View file

@ -61,7 +61,7 @@ These instructions will show how to setup the environment on OSX to the point wh
2. Install dependencies: 2. Install dependencies:
```bash ```bash
sudo port install readline jansson lua52 python311 bzip2 lz4 openssl11 arm-none-eabi-gcc arm-none-eabi-binutils coreutils qt5 qt5-qtbase pkgconfig sudo port install readline jansson lua52 python311 bzip2 lz4 openssl11 arm-none-eabi-gcc arm-none-eabi-binutils coreutils qt5 qt5-qtbase gd2 pkgconfig
``` ```
3. Clamp Python version for pkg-config 3. Clamp Python version for pkg-config

BIN
tools/lena.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View file

@ -535,6 +535,7 @@ while true; do
if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Test\(s\) \[ ok"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Test\(s\) \[ ok"; then break; fi
if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \[ ok"; then break; fi if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \[ ok"; then break; fi
if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \[ ok"; then break; fi if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \[ ok"; then break; fi
if ! CheckExecute "hf waveshare load" "$CLIENTBIN -c 'hf waveshare load -m 6 -f tools/lena.bmp -s dither.bmp' && echo '34ff55fe7257876acf30dae00eb0e439 dither.bmp' | md5sum -c" "dither.bmp: OK"; then break; fi
fi fi
echo -e "\n------------------------------------------------------------" echo -e "\n------------------------------------------------------------"
echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n" echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n"