mirror of
https://github.com/kwsch/pk3DS.git
synced 2026-10-02 13:57:14 -05:00
Refactoring & Beginnings of Compression
Using linq to make some code cleaner; likely runs a little slower but the tradeoff is good enough.
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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
|
||||
List<byte>raw = new List<byte>();
|
||||
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<s.Length ? count : data.Length % count).Replace('-', ' ');
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user