summaryrefslogtreecommitdiff
path: root/utils/zenutils/source/shared/cenc.cpp
diff options
context:
space:
mode:
authorMaurus Cuelenaere <mcuelenaere@gmail.com>2008-07-11 15:50:46 +0000
committerMaurus Cuelenaere <mcuelenaere@gmail.com>2008-07-11 15:50:46 +0000
commit14c7f45cdae826f88dc539c8c38dd95caf305731 (patch)
tree832da054b7cfb2dc6fd63339af736625f31d21aa /utils/zenutils/source/shared/cenc.cpp
parent7c84ede3781c27db73403bd6302f320c76a58c8c (diff)
downloadrockbox-14c7f45cdae826f88dc539c8c38dd95caf305731.zip
rockbox-14c7f45cdae826f88dc539c8c38dd95caf305731.tar.gz
rockbox-14c7f45cdae826f88dc539c8c38dd95caf305731.tar.bz2
rockbox-14c7f45cdae826f88dc539c8c38dd95caf305731.tar.xz
Add zook's ZenUtils to SVN
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18010 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'utils/zenutils/source/shared/cenc.cpp')
-rwxr-xr-xutils/zenutils/source/shared/cenc.cpp333
1 files changed, 333 insertions, 0 deletions
diff --git a/utils/zenutils/source/shared/cenc.cpp b/utils/zenutils/source/shared/cenc.cpp
new file mode 100755
index 0000000..932bee4
--- /dev/null
+++ b/utils/zenutils/source/shared/cenc.cpp
@@ -0,0 +1,333 @@
+/* zenutils - Utilities for working with creative firmwares.
+ * Copyright 2007 (c) Rasmus Ry <rasmus.ry{at}gmail.com>
+ *
+ * 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 2 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cenc.h"
+#include <firmware.h>
+#include <stdexcept>
+
+
+namespace {
+const byte CODE_MASK = 0xC0;
+const byte ARGS_MASK = 0x3F;
+
+const byte REPEAT_CODE = 0x00;
+const byte BLOCK_CODE = 0x40;
+const byte LONG_RUN_CODE = 0x80;
+const byte SHORT_RUN_CODE = 0xC0;
+
+const byte BLOCK_ARGS = 0x1F;
+const byte BLOCK_MODE = 0x20;
+
+
+void decode_run(byte* dst, word len, byte val,
+ int& dstidx)
+{
+ memset(dst + dstidx, val, len);
+ dstidx += len;
+}
+
+void decode_pattern(byte* src, byte* dst,
+ word len, int& srcidx, int& dstidx,
+ bool bdecode, int npasses)
+{
+ for (int i = 0; i < npasses; i++)
+ {
+ if (bdecode)
+ {
+ for (int j = 0; j < len; j++)
+ {
+ word c, d;
+ c = src[srcidx + j];
+ d = (c >> 5) & 7;
+ c = (c << 3) & 0xF8;
+ src[srcidx + j] = static_cast<byte>(c | d);
+ }
+ bdecode = false;
+ }
+ memcpy(dst + dstidx, src + srcidx, len);
+ dstidx += len;
+ }
+ srcidx += len;
+}
+}; //namespace
+
+int zen::cenc_decode(byte* src, int srclen, byte* dst, int dstlen)
+{
+ if (!src || !srclen || !dst || !dstlen)
+ {
+ throw std::invalid_argument("Invalid argument(s).");
+ }
+
+ int i = 0, j = 0;
+ do
+ {
+ word c, d, e;
+ c = src[i++];
+ switch (c & CODE_MASK)
+ {
+ case REPEAT_CODE: // 2 bytes
+ d = src[i++];
+ d = d + 2;
+
+ e = (c & ARGS_MASK) + 2;
+
+ decode_pattern(src, dst, e, i, j, false, d);
+ break;
+
+ case BLOCK_CODE: // 1/2/3 bytes
+ d = c & BLOCK_ARGS;
+ if (!(c & BLOCK_MODE))
+ {
+ e = src[i++];
+ e = (d << 8) + (e + 0x21);
+
+ d = static_cast<word>(i ^ j);
+ }
+ else
+ {
+ e = d + 1;
+
+ d = static_cast<word>(i ^ j);
+ }
+ if (d & 1)
+ {
+ i++;
+ }
+
+ decode_pattern(src, dst, e, i, j, true, 1);
+ break;
+
+ case LONG_RUN_CODE: // 3 bytes
+ d = src[i++];
+ e = ((c & ARGS_MASK) << 8) + (d + 0x42);
+
+ d = src[i++];
+ d = ((d & 7) << 5) | ((d >> 3) & 0x1F);
+
+ decode_run(dst, e, static_cast<byte>(d), j);
+ break;
+
+ case SHORT_RUN_CODE: // 2 bytes
+ d = src[i++];
+ d = ((d & 3) << 6) | ((d >> 2) & 0x3F);
+
+ e = (c & ARGS_MASK) + 2;
+
+ decode_run(dst, e, static_cast<byte>(d), j);
+ break;
+ };
+ } while (i < srclen && j < dstlen);
+
+ return j;
+}
+
+namespace {
+int encode_run(byte* dst, int& dstidx, byte val, int len, int dstlen)
+{
+ if (len < 2)
+ throw std::invalid_argument("Length is too small.");
+
+ int ret = 0;
+ if (len <= 0x41)
+ {
+ if ((dstidx + 2) > dstlen)
+ throw std::runtime_error("Not enough space to store run.");
+
+ dst[dstidx++] = SHORT_RUN_CODE | (((len - 2) & ARGS_MASK));
+ dst[dstidx++] = ((val >> 6) & 3) | ((val & 0x3F) << 2);
+
+ ret = 2;
+ }
+ else if (len <= 0x4041)
+ {
+ if ((dstidx + 3) > dstlen)
+ throw std::runtime_error("Not enough space to store run.");
+
+ byte b1 = (len - 0x42) >> 8;
+ byte b2 = (len - 0x42) & 0xFF;
+
+ dst[dstidx++] = LONG_RUN_CODE | ((b1 & ARGS_MASK));
+ dst[dstidx++] = b2;
+ dst[dstidx++] = ((val >> 5) & 7) | ((val & 0x1F) << 3);
+
+ ret = 3;
+ }
+ else
+ {
+ int long_count = len / 0x4041;
+ int short_len = len % 0x4041;
+ bool toosmall = short_len == 1;
+
+ int run_len = 0x4041;
+ for (int i = 0; i < long_count; i++)
+ {
+ if (toosmall && (i == (long_count-1)))
+ {
+ run_len--;
+ toosmall = false;
+ }
+ int tmp = encode_run(dst, dstidx, val, run_len, dstlen);
+ if (!tmp) return 0;
+ ret += tmp;
+ len -= run_len;
+ }
+
+ if (len)
+ {
+ int short_count = len / 0x41;
+ int short_rest = short_count ? (len % 0x41) : 0;
+ toosmall = short_rest == 1;
+
+ run_len = 0x41;
+ for (int i = 0; i < short_count; i++)
+ {
+ if (toosmall && (i == (short_count-1)))
+ {
+ run_len--;
+ toosmall = false;
+ }
+ int tmp = encode_run(dst, dstidx, val, run_len, dstlen);
+ if (!tmp) return 0;
+ ret += tmp;
+ len -= run_len;
+ }
+ int tmp = encode_run(dst, dstidx, val, len, dstlen);
+ if (!tmp) return 0;
+ ret += tmp;
+ len -= len;
+ }
+ }
+
+ return ret;
+}
+
+int encode_block(byte* dst, int& dstidx, byte* src, int& srcidx, int len,
+ int dstlen)
+{
+ if (len < 1)
+ throw std::invalid_argument("Length is too small.");
+
+ int startidx = dstidx;
+ if (len < 0x21)
+ {
+ if ((dstidx + 2 + len) > dstlen)
+ throw std::runtime_error("Not enough space to store block.");
+
+ dst[dstidx++] = BLOCK_CODE | BLOCK_MODE | ((len - 1) & BLOCK_ARGS);
+ if ((dstidx ^ srcidx) & 1)
+ dst[dstidx++] = 0;
+
+ for (int i = 0; i < len; i++)
+ {
+ byte c = src[srcidx++];
+ byte d = (c & 7) << 5;
+ c = (c & 0xF8) >> 3;
+ dst[dstidx++] = c | d;
+ }
+ }
+ else if (len < 0x2021)
+ {
+ if ((dstidx + 3 + len) > dstlen)
+ throw std::runtime_error("Not enough space to store block.");
+
+ dst[dstidx++] = BLOCK_CODE | (((len - 0x21) >> 8) & BLOCK_ARGS);
+ dst[dstidx++] = (len - 0x21) & 0xFF;
+ if ((dstidx ^ srcidx) & 1)
+ dst[dstidx++] = 0;
+
+ for (int i = 0; i < len; i++)
+ {
+ byte c = src[srcidx++];
+ byte d = (c & 7) << 5;
+ c = (c & 0xF8) >> 3;
+ dst[dstidx++] = c | d;
+ }
+ }
+ else
+ {
+ int longblocks = len / 0x2020;
+ int rest = len % 0x2020;
+ for (int i = 0; i < longblocks; i++)
+ {
+ int tmp = encode_block(dst, dstidx, src, srcidx, 0x2020, dstlen);
+ if (!tmp) return 0;
+ }
+ if (rest)
+ {
+ int shortblocks = rest / 0x20;
+ for (int i = 0; i < shortblocks; i++)
+ {
+ int tmp = encode_block(dst, dstidx, src, srcidx, 0x20, dstlen);
+ if (!tmp) return 0;
+ }
+ rest = rest % 0x20;
+ int tmp = encode_block(dst, dstidx, src, srcidx, rest, dstlen);
+ if (!tmp) return 0;
+ }
+ }
+
+ return (dstidx - startidx);
+}
+}; //namespace
+
+int zen::cenc_encode(byte* src, int srclen, byte* dst, int dstlen)
+{
+ if (!src || !srclen || !dst || !dstlen)
+ {
+ throw std::invalid_argument("Invalid argument(s).");
+ }
+
+ int i = 0, j = 0, k = 0;
+ word c, d, e;
+ int runlen = 0;
+ while (i < srclen && j < dstlen)
+ {
+ k = i;
+ c = src[i++];
+ runlen = 1;
+ while (i < srclen && src[i] == c)
+ {
+ runlen++;
+ i++;
+ }
+ if (runlen >= 2)
+ {
+ if (!encode_run(dst, j, c, runlen, dstlen))
+ return 0;
+ }
+ else
+ {
+ runlen = 0;
+ i = k;
+ while (i < (srclen - 1) && (src[i] != src[i + 1]))
+ {
+ runlen++;
+ i++;
+ }
+ if (i == (srclen - 1))
+ {
+ runlen++;
+ i++;
+ }
+ if (!encode_block(dst, j, src, k, runlen, dstlen))
+ return 0;
+ }
+ }
+
+ return j;
+}