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>
---
v5: rebased on latest master; incorporate Phil's comments on v4.
 libconcord/INSTALL.linux     |   4 +-
 libconcord/INSTALL.mac       |   2 +-
 libconcord/Makefile.am       |   2 +-
 libconcord/configure.ac      |   6 +-
 libconcord/libconcord.cpp    |  93 ++++++++++++++++++++++++++-
 libconcord/operationfile.cpp |  61 +++++++++---------
 libconcord/remote.h          |   2 +
 libconcord/remote_mh.cpp     | 145 +++++++++++++++++++++++++++----------------
 libconcord/xml_headers.h     |   1 +
 9 files changed, 220 insertions(+), 96 deletions(-)

diff --git a/libconcord/INSTALL.linux b/libconcord/INSTALL.linux
index 2d70194..0a7a365 100644
--- a/libconcord/INSTALL.linux
+++ b/libconcord/INSTALL.linux
@@ -7,7 +7,7 @@ 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.
 
 You also need hidapi which can be found at: https://github.com/signal11/hidapi
@@ -29,7 +29,7 @@ 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 325c379..10d215a 100644
--- a/libconcord/INSTALL.mac
+++ b/libconcord/INSTALL.mac
@@ -38,7 +38,7 @@ and do:
    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 96f3e1f..700a722 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 libusbhid.cpp libhidapi.cpp
 include_HEADERS = libconcord.h
-libconcord_la_LDFLAGS = -version-info 3:0:0 -l$(USBLIB) -lzzip
+libconcord_la_LDFLAGS = -version-info 3:0:0 -l$(USBLIB) -lzip
 UDEVROOT ?= /
 UDEVLIBDIR ?= $(UDEVROOT)/lib
 
diff --git a/libconcord/configure.ac b/libconcord/configure.ac
index e916a11..9cd4be9 100644
--- a/libconcord/configure.ac
+++ b/libconcord/configure.ac
@@ -67,11 +67,11 @@ then
   fi
   AC_MSG_ERROR([$errorstr])
 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_FILES([
     Makefile
diff --git a/libconcord/libconcord.cpp b/libconcord/libconcord.cpp
index 82b3afe..801e42c 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,86 @@ 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.  Note that in
+ * some cases (Harmony 300), the remote does not return the EOF bytes, so we
+ * manually add them ourselves (see ReadFlash() in remote_mh.cpp).
+ */
+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 {
+            char error_str[100];
+            zip_error_to_str(error_str, 100, zip_err, errno);
+            debug("Failed to create zip file %s (%s)", file_name, error_str);
+        }
+        return LC_ERROR_OS_FILE;
+    }
+    int index;
+
+    // Write XML
+    extern const char *mh_config_header;
+    // 100 is arbitrary - it should be plenty to hold the snprintf data below
+    int xml_buffer_len = strlen(mh_config_header) + 100;
+    char xml_buffer[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;
+    }
+
+    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 +1131,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..322f25a 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"
@@ -87,40 +87,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;
-                xml = new uint8_t[xml_size];
-                zzip_size_t len = zzip_file_read(fh, 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 = new uint8_t[data_size];
-                data_alloc = true;
-                zzip_size_t len = zzip_file_read(fh, data,
-                    data_size);
-            }
-            zzip_file_close(fh);
-        }
-    } else {
+    struct zip *zip = zip_open(file_name, 0, NULL);
+    if (!zip) {
         return LC_ERROR;
     }
-    zzip_dir_close(dir);
+
+    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];
+            int len = zip_fread(file, xml, xml_size);
+            debug("len is %d, xml is %p, and xmlsize is %d", len, xml,
+                  xml_size);
+        } else {
+            data_size = stat.size;
+            data = new uint8_t[data_size];
+            data_alloc = true;
+            int len = zip_fread(file, data, data_size);
+            debug("len is %d, data_size is %d", len, data_size);
+        }
+        zip_fclose(file);
+    }
+    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..98aab98 100644
--- a/libconcord/remote_mh.cpp
+++ b/libconcord/remote_mh.cpp
@@ -160,6 +160,27 @@ uint8_t get_seq(uint8_t &seq)
     return tmp;
 }
 
+// Sends the reset sequence message
+int reset_sequence(uint8_t seq, uint8_t param)
+{
+    const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] =
+        { 0xFF, 0x07, seq, 0x01, 0x01, param };
+    uint8_t rsp[MH_MAX_PACKET_SIZE];
+
+    if (HID_WriteReport(msg_reset_seq)) {
+        debug("Failed to write to remote");
+        return LC_ERROR_WRITE;
+    }
+    if (HID_ReadReport(rsp, MH_TIMEOUT)) {
+        debug("Failed to read from remote");
+        return LC_ERROR_READ;
+    }
+    debug("msg_reset_seq");
+    debug_print_packet(rsp);
+
+    return 0;
+}
+
 int CRemoteMH::ReadFile(const char *filename, uint8_t *rd, const uint32_t 
rdlen,
                         int *data_read, uint8_t start_seq)
 {
@@ -278,19 +299,8 @@ int CRemoteMH::ReadFile(const char *filename, uint8_t *rd, 
const uint32_t rdlen,
     }
     debug("data_read=%d", *data_read);
 
-    /* send reset sequence message */
-    const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] =
-        { 0xFF, 0x07, get_seq(seq), 0x01, 0x01, param };
-    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);
+    if (err = reset_sequence(get_seq(seq), param))
+        return err;
 
     return 0;
 }
@@ -393,19 +403,8 @@ int CRemoteMH::WriteFile(const char *filename, uint8_t *wr,
     debug("after writing file");
     debug_print_packet(rsp);
 
-    /* send reset sequence message */
-    const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] =
-        { 0xFF, 0x07, get_seq(seq), 0x01, 0x01, param };
-    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);
+    if (err = reset_sequence(get_seq(seq), param))
+        return err;
 
     return 0;
 }
@@ -492,8 +491,28 @@ 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);
+
+    // In ReadFlash() we add an extra four bytes to the end of the config file
+    // buffer, so we include space for those bytes here.
+    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 +534,52 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO 
&hid, lc_callback cb,
         make_serial(guid, ri);
     }
 
+    if (err = reset_sequence(0x01, 0x06))
+        return err;
+
     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;
+
+    /*
+     * When returning configs, some remotes (200) return the config + EOF bytes
+     * + extra padding, while other remotes (300) return just the config (with
+     * no EOF bytes or padding).  Here, to handle the latter case, we add the
+     * EOF bytes to the end of the buffer.  That way, when we check the length
+     * of the config in _mh_get_config_len() by searching for the first 
instance
+     * of the EOF bytes, we will find the correct length of the config in both
+     * cases.
+     */
+    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)
@@ -674,19 +731,8 @@ int CRemoteMH::LearnIR(uint32_t *freq, uint32_t 
**ir_signal,
     debug("msg_stop");
     debug_print_packet(rsp);
 
-    /* send reset sequence message */
-    const uint8_t msg_reset_seq[MH_MAX_PACKET_SIZE] =
-        { 0xFF, 0x07, 0x03, 0x01, 0x01, 0x0C };
-    if (HID_WriteReport(msg_reset_seq) != 0) {
-        debug("Failed to write to remote");
-        err = LC_ERROR_WRITE;
-    }
-    if (HID_ReadReport(rsp) != 0) {
-        debug("Failed to read from remote");
-        err = LC_ERROR_READ;
-    }
-    debug("msg_reset_seq");
-    debug_print_packet(rsp);
+    if (err = reset_sequence(0x03, 0x0C))
+        return err;
 
     if (cb && !err) {
         cb(cb_stage, 1, 1, 1, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
@@ -882,19 +928,8 @@ int CRemoteMH::UpdateConfig(const uint32_t len, const 
uint8_t *wr,
     cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 3, 4,
        LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
 
-    /* write msg 7 */
-    const uint8_t msg_7[MH_MAX_PACKET_SIZE] =
-        { 0xFF, 0x07, get_seq(seq), 0x01, 0x01, 0x05 };
-    if ((err = HID_WriteReport(msg_7))) {
-        debug("Failed to write to remote");
-        return LC_ERROR_WRITE;
-    }
-    if ((err = HID_ReadReport(rsp, MH_TIMEOUT))) {
-        debug("Failed to read from remote");
-        return LC_ERROR_READ;
-    }
-    debug("msg_7");
-    debug_print_packet(rsp);
+    if (err = reset_sequence(get_seq(seq), 0x05))
+        return err;
 
     cb(LC_CB_STAGE_FINALIZE_UPDATE, cb_count++, 4, 4,
        LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
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


------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60133471&iu=/4140/ostg.clktrk
_______________________________________________
concordance-devel mailing list
concordance-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/concordance-devel

Reply via email to