diff --git a/KaitaiStream.cs b/KaitaiStream.cs
index 0863944..013517e 100644
--- a/KaitaiStream.cs
+++ b/KaitaiStream.cs
@@ -50,6 +50,11 @@ public KaitaiStream(byte[] data) : base(new MemoryStream(data))
///
static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
+ static KaitaiStream()
+ {
+ compute_single_rotations();
+ }
+
#endregion
#region Stream positioning
@@ -520,40 +525,107 @@ public byte[] ProcessXor(byte[] data, byte[] key)
int kl = key.Length;
byte[] result = new byte[dl];
- for (int i = 0, j = 0; i < dl; i++, j = i % kl)
+ for (int i = 0; i < dl; i++)
{
- result[i] = (byte)(data[i] ^ key[j]);
+ result[i] = (byte)(data[i] ^ key[i % kl]);
}
return result;
}
+ ///
+ /// Used internally.
+ ///
+ private static byte[][] precomputed_single_rotations;
+
+ ///
+ /// Used internally.
+ ///
+ private static void compute_single_rotations()
+ {
+ precomputed_single_rotations = new byte[8][];
+ for (int amount = 1; amount < 8; amount++)
+ {
+ byte[] translate = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ // formula taken from: http://stackoverflow.com/a/812039
+ translate[i] = (byte) ((i << amount) | (i >> (8 - amount)));
+ }
+ precomputed_single_rotations[amount] = translate;
+ }
+ }
+
///
/// Perform circular left rotation shift for a given data by a given amount of bits.
/// Pass a negative amount to rotate right.
+ /// WARNING: May return same byte array if amount is zero (modulo-wise).
///
/// The data to rotate, as byte array
- /// The amount to rotate by (in bits), as integer
- /// The size of group in which rotation happens, as non-negative integer
+ /// The amount to rotate by (in bits), as integer, negative for right rotation
+ /// The size of group in which rotation happens, as positive integer
public byte[] ProcessRotateLeft(byte[] data, int amount, int groupSize)
{
- if (amount > 7 || amount < -7) throw new ArgumentException("Rotation of more than 7 cannot be performed.", "amount");
- if (amount < 0) amount += 8; // Rotation of -2 is the same as rotation of +6
+ if (groupSize < 1)
+ throw new Exception("group size must be at least 1 to be valid");
+
+ amount = Mod(amount, groupSize * 8);
+ if (amount == 0)
+ return data;
+
+ int amount_bytes = amount / 8;
+ int dl = data.Length;
+ byte[] result = new byte[dl];
- byte[] r = new byte[data.Length];
- switch (groupSize)
+ if (groupSize == 1)
{
- case 1:
- for (int i = 0; i < data.Length; i++)
+ byte[] translate = precomputed_single_rotations[amount];
+
+ for (int i = 0; i < dl; i++)
+ {
+ result[i] = translate[data[i]];
+ }
+ return result;
+ }
+
+ if (dl % groupSize != 0)
+ throw new Exception("data length must be a multiple of group size");
+
+ if (amount % 8 == 0)
+ {
+ int[] indices = new int[groupSize];
+ for (int i = 0; i < groupSize; i++)
+ {
+ indices[i] = (i + amount_bytes) % groupSize;
+ }
+ for (int i = 0; i < dl; i += groupSize)
+ {
+ for (int k = 0; k < groupSize; k++)
{
- byte bits = data[i];
- // http://stackoverflow.com/a/812039
- r[i] = (byte) ((bits << amount) | (bits >> (8 - amount)));
+ result[i+k] = data[i + indices[k]];
}
- break;
- default:
- throw new NotImplementedException(string.Format("Unable to rotate a group of {0} bytes yet", groupSize));
+ }
+ return result;
+ }
+
+ {
+ int amount1 = amount % 8;
+ int amount2 = 8 - amount1;
+ int[] indices1 = new int[groupSize];
+ int[] indices2 = new int[groupSize];
+ for (int i = 0; i < groupSize; i++)
+ {
+ indices1[i] = (i + amount_bytes) % groupSize;
+ indices2[i] = (i + 1 + amount_bytes) % groupSize;
+ }
+ for (int i = 0; i < dl; i += groupSize)
+ {
+ for (int k = 0; k < groupSize; k++)
+ {
+ result[i+k] = (byte) ((data[i + indices1[k]] << amount1) | (data[i + indices2[k]] >> amount2));
+ }
+ }
+ return result;
}
- return r;
}
///