diff --git a/pk3DS/Subforms/OWSE.cs b/pk3DS/Subforms/OWSE.cs index 03e3a03..2c7f812 100644 --- a/pk3DS/Subforms/OWSE.cs +++ b/pk3DS/Subforms/OWSE.cs @@ -124,7 +124,7 @@ private void getScriptData() { byte[] ScriptData = data; uint length = BitConverter.ToUInt32(ScriptData, 0); - RTB_MS.Text = Util.getHexString(ScriptData); + RTB_MS.Lines = Scripts.getHexLines(ScriptData, 8); int start = BitConverter.ToInt32(ScriptData, 0xC); @@ -145,7 +145,7 @@ private void getScriptData() int diff = declength - decompressed.Length; Array.Resize(ref decompressed, declength); - RTB_MSCMD.Text = Scripts.getu32line(decompressed); + RTB_MSCMD.Lines = Scripts.getHexLines(decompressed); RTB_MSCMD.Width = RTB_MSCMD.Lines.Length < 20 ? 75 : 85; if (diff > 20 || diff < -20) RTB_MSCMD.Text = "DCMP FAIL"; @@ -194,7 +194,7 @@ private void getOWSData() { byte[] ScriptData = OWScriptData; uint length = BitConverter.ToUInt32(ScriptData, 0); - RTB_S.Text = Util.getHexString(ScriptData); + RTB_S.Lines = Scripts.getHexLines(ScriptData, 8); // RTB_S.Width = RTB_S.Text.Length < 25 * 3 * 16 ? 245 : 260; int start = BitConverter.ToInt32(ScriptData, 0xC); @@ -216,7 +216,7 @@ private void getOWSData() int diff = declength - decompressed.Length; Array.Resize(ref decompressed, declength); - RTB_OWSCMD.Text = Scripts.getu32line(decompressed); + RTB_OWSCMD.Lines = Scripts.getHexLines(decompressed); RTB_OWSCMD.Width = RTB_OWSCMD.Lines.Length < 20 ? 75 : 85; if (diff > 20 || diff < -20) RTB_OWSCMD.Text = "DCMP FAIL"; diff --git a/pk3DS/Tools/Scripts.cs b/pk3DS/Tools/Scripts.cs index 75f6f3d..d422a25 100644 --- a/pk3DS/Tools/Scripts.cs +++ b/pk3DS/Tools/Scripts.cs @@ -1,116 +1,140 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; namespace pk3DS { - // Big thanks to FireFly for figuring out the 7-bit compression routine for scripts. + // Big thanks to FireFly for figuring out the 7/6-bit compression routine for scripts. class Scripts { + // Decompression internal static byte[] decompressScript(byte[] data) { - using (BinaryReader br = new BinaryReader(new MemoryStream(data))) + if (data == null || data.Length % 4 != 0) // Bad Input + return null; using (MemoryStream mn = new MemoryStream()) using (BinaryWriter bw = new BinaryWriter(mn)) { // Read away! - while (br.BaseStream.Position < br.BaseStream.Length) + int pos = 0; + while (pos < data.Length) { // Read until the top bit is not set - Listraw = new List(); - byte val = br.ReadByte(); raw.Add(val); - while ((val >> 7) > 0) - { - if (br.BaseStream.Position == br.BaseStream.Length) return mn.ToArray(); - val = br.ReadByte(); raw.Add(val); - } - byte[] compressedBytes = raw.ToArray(); - + byte[] cb = readCompressed(data, pos); + pos += cb.Length; // Interpret the bytecode - val = compressedBytes[0]; - - if ((val & 0x40) > 0) // Signed Parameter - { - // Check the next bytecode - if (compressedBytes.Length > 1 && (compressedBytes[1] >> 7) > 0) // Many-bits-required command - { - // 2 Byte Signed Parameter - int cmd = (compressedBytes[0] & 0x3 << 14) | (compressedBytes[1] & 0x7F << 7) | compressedBytes[2]; // 16 Bits total - bw.Write(BitConverter.GetBytes(cmd), 0, 2); - - int deviation = ((val & 0x3F) - 0x40) >> 2; // Lowest 2 bits have already been used for the command - bw.Write(BitConverter.GetBytes(deviation), 0, 2); // 16 Bits - } - else if ((val >> 7) > 0) - { - // 3 Byte Signed Parameter - int cmd = ((compressedBytes[0] << 7) | compressedBytes[1]); - bw.Write(BitConverter.GetBytes(cmd), 0, 1); // 8 Bits Total - - int deviation = ((val & 0x3F) - 0x40) >> 1; // Lowest bit has already been used for the command - bw.Write(BitConverter.GetBytes(deviation), 0, 3); // 24 Bits - } - else - { - // 4 Byte Signed Parameter - int deviation = ((val & 0x3F) - 0x40) >> 0; // No bits have already been used; no command - bw.Write(BitConverter.GetBytes(deviation), 0, 4); // 32 Bits - } - } - else if ((val >> 7) > 0) // Manybit - { - Array.Reverse(compressedBytes); - int cmd = 0; - for (int i = 0; i < compressedBytes.Length; i++) - { - cmd |= ((compressedBytes[i] & 0x7F) << (7*i)); - } - - bw.Write(BitConverter.GetBytes((uint)cmd), 0, 4); - } - else // Literal - { - bw.Write(BitConverter.GetBytes((uint)val), 0, 4); - } - - uint result = BitConverter.ToUInt32(mn.GetBuffer(), (int) bw.BaseStream.Position - 4); + byte[] db = decompressBytes(cb); + // Write Bytes + bw.Write(db); } return mn.ToArray(); } } + internal static byte[] readCompressed(byte[] data, int pos) + { + byte[] c1 = data.Skip(pos).TakeWhile(b => b >> 7 > 0).ToArray(); // Take while >= 0x80 + return c1.Concat(data.Skip(pos + c1.Count()).Take(1)).ToArray(); // Take another + } + internal static byte[] decompressBytes(byte[] cb) + { + byte[] db = new byte[0]; + + if ((cb[0] & 0x40) > 0) // Signed Parameter + { + // Check the next bytecode + if (cb.Length > 1 && (cb[1] >> 7) > 0) // Many-bits-required command + { + // 2 Byte Signed Parameter + int cmd = (cb[0] & 0x3 << 14) | (cb[1] & 0x7F << 7) | cb[2]; // 16 Bits total + db = db.Concat(BitConverter.GetBytes(cmd).Take(2)).ToArray(); // 16 Bits + + int dev = ((cb[0] & 0x3F) - 0x40) >> 2; // Lowest 2 bits have already been used for the command + db = db.Concat(BitConverter.GetBytes(dev).Take(2)).ToArray(); // 16 Bits + } + else if ((cb[0] >> 7) > 0) // Signed Command + { + // 3 Byte Signed Parameter + int cmd = ((cb[0] << 7) | cb[1]); + db = db.Concat(BitConverter.GetBytes(cmd).Take(1)).ToArray(); // 8 Bits Total + + int dev = ((cb[0] & 0x3F) - 0x40) >> 1; // Lowest bit has already been used for the command + db = db.Concat(BitConverter.GetBytes(dev).Take(3)).ToArray(); // 24 Bits + } + else // Signed Value + { + // 4 Byte Signed Parameter + int dev = ((cb[0] & 0x3F) - 0x40) >> 0; // No bits have already been used; no command + db = db.Concat(BitConverter.GetBytes(dev).Take(4)).ToArray(); // 32 Bits + } + } + else if ((cb[0] >> 7) > 0) // Manybit + { + Array.Reverse(cb); + int cmd = 0; + for (int i = 0; i < cb.Length; i++) + cmd |= ((cb[i] & 0x7F) << (7 * i)); + + db = db.Concat(BitConverter.GetBytes((uint)cmd).Take(4)).ToArray(); + } + else // Literal + { + db = db.Concat(BitConverter.GetBytes((uint)cb[0]).Take(4)).ToArray(); + } + return db; + } + + // Compression internal static byte[] compressScript(byte[] data) { - // Need decompression functional first. - using (BinaryReader br = new BinaryReader(new MemoryStream(data))) + if (data == null || data.Length % 4 != 0) // Bad Input + return null; using (MemoryStream mn = new MemoryStream()) using (BinaryWriter bw = new BinaryWriter(mn)) { - // Compress away! - while (br.BaseStream.Position < br.BaseStream.Length) + int pos = 0; + while (pos < data.Length) { - // Read the command to compress - uint val = br.ReadUInt32(); - - // Write byte if more bits are needed - while ((val >> 7) > 0) // 8th+ bit is set - { - bw.Write((byte)((val & 0x7F) | 0x80)); - val >>= 7; - } - // Write the remaining bits - bw.Write((byte)(val & 0x7F)); + byte[] db = data.Skip(pos+=4).Take(4).ToArray(); + byte[] cb = compressBytes(db); + bw.Write(cb); } return mn.ToArray(); } } - - internal static string getu32line(byte[] data) + internal static byte[] compressBytes(byte[] cb) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < data.Length; i+= 4) - sb.Append(BitConverter.ToString(data, i, 4).Replace('-', ' ') + "\r\n"); - return sb.ToString(); + short cmd = BitConverter.ToInt16(cb, 0); + short val = BitConverter.ToInt16(cb, 2); + + byte[] blank = new byte[0]; + bool manyb = cmd > 0x3F; // manybit + bool sign4 = cmd < 0 && !manyb; // 4 byte signed + bool sign3 = cmd < 0 && manyb; // 3 byte signed + bool sign2 = val < 0; // 2 byte signed + bool liter = cmd >= 0 && cmd < 0x40; + + if (sign4) + return blank; + if (sign3) + return blank; + if (sign2) + return blank; + if (manyb) + return blank; + if (liter) + return blank; + + return blank; + } + + // General Viewing Utility + internal static string[] getHexLines(byte[] data, int count = 4) + { + // Generates an x-byte wide space separated string array; leftovers included at the end. + string[] s = new string[data.Length/count + ((data.Length % count > 0) ? 1 : 0)]; + for (int i = 0; i < s.Length;i++) + s[i] += BitConverter.ToString(data, i*count, i+1