In order to write proper MH config files, we needed to be able to write zip files; thus libzzip was replaced with libzip.
Signed-off-by: Scott Talbert <s...@techie.net> --- Changes in v3: removed all uses of libzzip and replaced with libzip; switched to use new ReadFile function which eliminated a lot of duplicated code. libconcord/INSTALL.linux | 4 +- libconcord/INSTALL.mac | 2 +- libconcord/Makefile.am | 2 +- libconcord/configure.ac | 6 +-- libconcord/libconcord.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++- libconcord/operationfile.cpp | 44 ++++++++++------------ libconcord/remote.h | 2 + libconcord/remote_mh.cpp | 72 +++++++++++++++++++++++++++++++++-- libconcord/xml_headers.h | 1 + 9 files changed, 187 insertions(+), 35 deletions(-) diff --git a/libconcord/INSTALL.linux b/libconcord/INSTALL.linux index 06a0385..7e622ed 100644 --- a/libconcord/INSTALL.linux +++ b/libconcord/INSTALL.linux @@ -7,14 +7,14 @@ source, see the instructions below. 0. INSTALL REQUIRED SOFTWARE -You *MUST* install libusb and libzzip. These libraries are in most +You *MUST* install libusb and libzip. These libraries are in most distributions, so apt-get/yum/up2date/urpmi/etc. it. Also, if you are using 900/1000/1100 remotes, then dnsmasq is a requirement, as well as installing the udev support files for libconcord (see below). If you're compiling libconcord from source, you'll also need the development -packages - usually libusb-dev or libusb-devel (and libzzip-dev/libzzip-devel), +packages - usually libusb-dev or libusb-devel (and libzip-dev/libzip-devel), depending on your distribution. 1. BUILD LIBCONCORD diff --git a/libconcord/INSTALL.mac b/libconcord/INSTALL.mac index 0c74b92..d69265f 100644 --- a/libconcord/INSTALL.mac +++ b/libconcord/INSTALL.mac @@ -30,7 +30,7 @@ that first. It is also straight forward: make sudo make install -Finally, you will need to install libzzip, which again is straight forward: +Finally, you will need to install libzip, which again is straight forward: ./configure --prefix=/usr make sudo make install diff --git a/libconcord/Makefile.am b/libconcord/Makefile.am index 808484c..79182a9 100644 --- a/libconcord/Makefile.am +++ b/libconcord/Makefile.am @@ -6,7 +6,7 @@ libconcord_la_SOURCES = remote.cpp remote_z.cpp libconcord.cpp binaryfile.cpp \ remote_info.h web.h protocol.h remote.h usblan.h xml_headers.h \ operationfile.cpp remote_mh.cpp include_HEADERS = libconcord.h -libconcord_la_LDFLAGS = -version-info 3:0:0 -lusb -lzzip +libconcord_la_LDFLAGS = -version-info 3:0:0 -lusb -lzip UDEVROOT ?= / UDEVLIBDIR ?= $(UDEVROOT)/lib diff --git a/libconcord/configure.ac b/libconcord/configure.ac index bd0477d..51107ef 100644 --- a/libconcord/configure.ac +++ b/libconcord/configure.ac @@ -28,11 +28,11 @@ if test $a == 0 then AC_MSG_ERROR([Error, libusb is missing!]) fi -AC_CHECK_HEADER(zzip/lib.h, [], [a=0]) -AC_CHECK_LIB(zzip, zzip_dir_open, [], [a=0]) +AC_CHECK_HEADER(zip.h, [], [a=0]) +AC_CHECK_LIB(zip, zip_open, [], [a=0]) if test $a == 0 then - AC_MSG_ERROR([Error, libzzip is missing!]) + AC_MSG_ERROR([Error, libzip is missing!]) fi AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ diff --git a/libconcord/libconcord.cpp b/libconcord/libconcord.cpp index 82b3afe..99c3ae3 100644 --- a/libconcord/libconcord.cpp +++ b/libconcord/libconcord.cpp @@ -29,7 +29,7 @@ #include <string.h> #include <stdlib.h> #include <errno.h> -#include <zzip/lib.h> +#include <zip.h> #include <list> #ifndef WIN32 #include <unistd.h> @@ -1043,6 +1043,82 @@ int _write_config_to_remote(lc_callback cb, void *cb_arg, uint32_t cb_stage) return 0; } +/* + * When MH configs are read from the remote, sometimes the remote returns extra + * data after the end of the config file proper. This function searches for + * the sequence of bytes that indicates the end of the config and returns the + * real length of the config that should be written out to disk. + */ +uint32_t _mh_get_config_len(uint8_t *in, uint32_t size) +{ + for (int i = 0; (i + 3) < size; i++) { + if (!memcmp(&in[i], MH_EOF_BYTES, 4)) { + return i + 4; + } + } + debug("Failed to find MH config EOF sequence"); + return 0; +} + +int _mh_write_config_to_file(uint8_t *in, uint32_t size, char *file_name) +{ + int zip_err; + struct zip *zip = zip_open(file_name, ZIP_CREATE | ZIP_EXCL, &zip_err); + if (!zip) { + if (zip_err == ZIP_ER_EXISTS) { + printf("Error: file %s already exists\n", file_name); + } else { + debug("Failed to create zip file %s", file_name); + } + return LC_ERROR_OS_FILE; + } + int index; + + // Write XML + extern const char *mh_config_header; + int xml_buffer_len = strlen(mh_config_header) + 100; + char *xml_buffer = new char[xml_buffer_len]; + uint16_t checksum = mh_get_checksum(in, size); + int xml_len = snprintf(xml_buffer, xml_buffer_len, mh_config_header, + size, size - 6, checksum, ri.skin); + if (xml_len >= xml_buffer_len) { + debug("Error, XML buffer length exceeded"); + return LC_ERROR; + } + struct zip_source *xml = zip_source_buffer(zip, xml_buffer, xml_len, 0); + if (!xml) { + debug("Failed to create zip_source_buffer for XML file"); + return LC_ERROR_OS_FILE; + } + index = zip_add(zip, "Description.xml", xml); + if (index == -1) { + debug("Error writing XML to zip file"); + zip_source_free(xml); + return LC_ERROR_OS_FILE; + } + + // Write EzHex file + struct zip_source *ezhex = zip_source_buffer(zip, in, size, 0); + if (!ezhex) { + debug("Failed to create zip_source_buffer for EzHex file"); + return LC_ERROR_OS_FILE; + } + index = zip_add(zip, "Result.EzHex", ezhex); + if (index == -1) { + debug("Error writing EzHex to zip file"); + zip_source_free(ezhex); + return LC_ERROR_OS_FILE; + } + + if (zip_close(zip) != 0) { + debug("Error closing zip file"); + return LC_ERROR_OS_FILE; + } + + delete[] xml_buffer; + return 0; +} + int write_config_to_remote(lc_callback cb, void *cb_arg) { return _write_config_to_remote(cb, cb_arg, LC_CB_STAGE_WRITE_CONFIG); @@ -1051,6 +1127,17 @@ int write_config_to_remote(lc_callback cb, void *cb_arg) int write_config_to_file(uint8_t *in, uint32_t size, char *file_name, int binary) { + // If this is an MH remote, need to find the real end of the binary + if (is_mh_remote()) { + size = _mh_get_config_len(in, size); + ri.config_bytes_used = size; + } + + // If this is an MH remote, need to write out zip file with XML/binary + if (!binary && is_mh_remote()) { + return _mh_write_config_to_file(in, size, file_name); + } + binaryoutfile of; if (of.open(file_name) != 0) { diff --git a/libconcord/operationfile.cpp b/libconcord/operationfile.cpp index df5f176..461174d 100644 --- a/libconcord/operationfile.cpp +++ b/libconcord/operationfile.cpp @@ -22,7 +22,7 @@ #include <stdlib.h> #include <string.h> -#include <zzip/lib.h> +#include <zip.h> #include <string> #include "libconcord.h" @@ -88,39 +88,35 @@ int find_config_binary(uint8_t *config, uint32_t config_size, int OperationFile::ReadZipFile(char *file_name) { /* TODO: error checking */ - zzip_error_t zip_err; - ZZIP_DIR *dir = zzip_dir_open(file_name, &zip_err); - if (dir) { - ZZIP_DIRENT dirent; - while (zzip_dir_read(dir, &dirent)) { - ZZIP_FILE *fh = zzip_file_open(dir, dirent.d_name, 0); - if ((strcmp(dirent.d_name, "Data.xml") == 0) || - (strcmp(dirent.d_name, "Description.xml") == 0)) { - debug("Internal file is %s", dirent.d_name); - debug("Size is %d", dirent.st_size); - xml_size = dirent.st_size; + struct zip *zip = zip_open(file_name, 0, NULL); + if (zip) { + struct zip_stat stat; + zip_uint64_t num_entries = zip_get_num_entries(zip, 0); + for (zip_uint64_t i = 0; i < num_entries; i++) { + zip_stat_index(zip, i, 0, &stat); + struct zip_file *file = zip_fopen(zip, stat.name, 0); + if ((strcmp(stat.name, "Data.xml") == 0) || + (strcmp(stat.name, "Description.xml") == 0)) { + debug("Internal file is %s", stat.name); + debug("Size is %d", stat.size); + xml_size = stat.size; xml = new uint8_t[xml_size]; - zzip_size_t len = zzip_file_read(fh, xml, + int len = zip_fread(file, xml, xml_size); + debug("len is %d, xml is %p, and xmlsize is %d", len, xml, xml_size); - debug("ERR IS: %s, len was %d", - zzip_strerror_of(dir), len); - debug("xml is %d and xmlsize is %d", xml, - xml_size); - //debug("%s%s%s%s", xml[0], xml[1], xml[2], - // xml[3]); } else { - data_size = dirent.st_size; + data_size = stat.size; data = new uint8_t[data_size]; data_alloc = true; - zzip_size_t len = zzip_file_read(fh, data, - data_size); + int len = zip_fread(file, data, data_size); + debug("len is %d, data_size is %d", len, data_size); } - zzip_file_close(fh); + zip_fclose(file); } } else { return LC_ERROR; } - zzip_dir_close(dir); + zip_close(zip); return 0; } diff --git a/libconcord/remote.h b/libconcord/remote.h index 44412cf..5d835ab 100644 --- a/libconcord/remote.h +++ b/libconcord/remote.h @@ -35,6 +35,7 @@ which is 1 (num params) + 3 (3 parameter size bytes) + 1 (param 1) + 1024 (param 2) + 4 (param 3) = 1033. */ #define USBNET_MAX_PACKET_SIZE 1033 +const uint8_t MH_EOF_BYTES[] = { 0x50, 0x54, 0x59, 0x59 }; /* * limits for IR signal learning, stop when any is reached: @@ -151,6 +152,7 @@ void setup_ri_pointers(TRemoteInfo &ri); void make_serial(uint8_t *ser, TRemoteInfo &ri); int LearnIRInnerLoop(uint32_t *freq, uint32_t **ir_signal, uint32_t *ir_signal_length, uint8_t seq); +uint16_t mh_get_checksum(uint8_t* rd, const uint32_t len); class CRemoteBase // Base class for all remotes { diff --git a/libconcord/remote_mh.cpp b/libconcord/remote_mh.cpp index c6b0ca0..ef5b040 100644 --- a/libconcord/remote_mh.cpp +++ b/libconcord/remote_mh.cpp @@ -492,8 +492,29 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid, lc_callback cb, cb(cb_stage, cb_count++, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL); } - ri.config_bytes_used = 0; - ri.max_config_size = 1; + // Send the read config message to find the config bytes used. + const uint8_t msg_read_config[MH_MAX_PACKET_SIZE] = + { 0xFF, 0x01, 0x00, 0x03, 0x80, '/', 'c', 'f', 'g', '/', + 'u', 's', 'e', 'r', 'c', 'f', 'g', 0x00, 0x80, 'R', 0x00 }; + if ((err = HID_WriteReport(msg_read_config))) { + debug("Failed to write to remote"); + return LC_ERROR_WRITE; + } + + if ((err = HID_ReadReport(rsp))) { + debug("Failed to read from remote"); + return LC_ERROR_READ; + } + debug("msg_read_config"); + debug_print_packet(rsp); + + // There are four extra bytes at the end of every MH config file - + // we add an extra four here so there is space in ReadFlash() to add + // those bytes. + ri.config_bytes_used = (rsp[7] << 24) + (rsp[8] << 16) + (rsp[9] << 8) + + rsp[10] + 4; + debug("ri.config_bytes_used = %d", ri.config_bytes_used); + ri.max_config_size = (ri.flash->size << 10); ri.valid_config = 1; if (cb) { @@ -515,14 +536,59 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid, lc_callback cb, make_serial(guid, ri); } + /* reset the sequence number to 0 */ + const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] = + { 0xFF, 0x07, 0x01, 0x01, 0x01, 0x06 }; + if ((err = HID_WriteReport(msg_reset_seq))) { + debug("Failed to write to remote"); + return LC_ERROR_WRITE; + } + + if ((err = HID_ReadReport(rsp))) { + debug("Failed to read from remote"); + return LC_ERROR_READ; + } + debug("msg_reset_seq"); + debug_print_packet(rsp); + return 0; } +// Calculates the XOR checksum for a config read from the remote. +uint16_t mh_get_checksum(uint8_t* rd, const uint32_t len) +{ + // This is the "SEED" that all the configs from the website use. + uint16_t cksum = 0x4321; + // The part of the config that gets checksummed is consistently 6 bytes + // less than the length of the config. Since we are checksumming two + // bytes at a time, we stop when i == len - 7, which is the same as + // i + 1 == len - 6. In the case of odd lengths, we skip the last byte. + for (int i = 0; i < (len - 7); i += 2) { + uint16_t j = (rd[i+1] << 8) + rd[i]; + cksum ^= j; + } + debug("CHECKSUM=0x%04x", cksum); + return cksum; +} + int CRemoteMH::ReadFlash(uint32_t addr, const uint32_t len, uint8_t *rd, unsigned int protocol, bool verify, lc_callback cb, void *cb_arg, uint32_t cb_stage) { - return LC_ERROR_UNSUPP; + int err = 0; + int data_read; + + if (err = ReadFile("/cfg/usercfg", rd, len, &data_read, 0x00)) + return err; + + /* + * There are four extra bytes at the end of every config file, but they + * are not present when read from the remote. Add them here. + */ + if (rd) + memcpy(rd + len - 4, MH_EOF_BYTES, 4); + + return 0; } int CRemoteMH::InvalidateFlash(lc_callback cb, void *cb_arg, uint32_t lc_stage) diff --git a/libconcord/xml_headers.h b/libconcord/xml_headers.h index aa2d663..6f79253 100644 --- a/libconcord/xml_headers.h +++ b/libconcord/xml_headers.h @@ -129,6 +129,7 @@ const char *config_header="\ </INFORMATION>\r\n"; +const char *mh_config_header = "<DATA><FILES><FILE NAME=\"Result.EzHex\" SIZE=\"%i\" PATH=\"/cfg/usercfg\" VERSION=\"1\" FW_VERSION=\"9.5\" OPERATIONTYPE=\"userconfiguration\"><CHECKSUM SEED=\"0x4321\" OFFSET=\"0x0\" LENGTH=\"0x%04x\" EXPECTEDVALUE=\"0x%04x\" TYPE=\"XOR\"/></FILE></FILES><INTENDED><SKIN>%i</SKIN></INTENDED><ORDER><ORDER_ELEMENT NAME=\"Result.EzHex\" RESET=\"true\"/></ORDER></DATA>"; //User-Agent: HarmonyBrowser/7.7.0 (Build 0; UpdatedFrom 7.3.0.15; Skin logitech; Windows Vista 6.1; x86; en; rv: 1.8.0.2) Gecko/20060125\r\n\ -- 1.8.3.1 ------------------------------------------------------------------------------ LIMITED TIME SALE - Full Year of Microsoft Training For Just $49.99! 1,500+ hours of tutorials including VisualStudio 2012, Windows 8, SharePoint 2013, SQL 2012, MVC 4, more. BEST VALUE: New Multi-Library Power Pack includes Mobile, Cloud, Java, and UX Design. Lowest price ever! Ends 9/22/13. http://pubads.g.doubleclick.net/gampad/clk?id=64545871&iu=/4140/ostg.clktrk _______________________________________________ concordance-devel mailing list concordance-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/concordance-devel