public static int FindLengthOfMIO0Block(byte[] data, int offsetOfMIO0) { //Load the header info MIO0Header header = new MIO0Header(data, offsetOfMIO0); //Count all the uncompressed bytes int uncompressedCount = 0; for (int i = 0x10; i < header.CompLoc; i++) { for (int j = 7; j >= 0; j--) { //Check for one if ((data[i + offsetOfMIO0] & (byte)(1 << j)) != 0) { uncompressedCount++; } } } //Make it line up to 4-byte address int length = uncompressedCount + (int)header.RawLoc; if (length % 4 != 0) length += 4 - (length % 4); return length; }
public static byte[] Encode(byte[] rawData, byte padToAddress = 4) { //To do: describe this algorithm // for now, just see here: http://wiki.origami64.net/sm64:mio0 int minByteClump = 3; int maxByteClump = 18; int maxReadBack = 4096; ClumpInfo currentBestClump; List<ClumpInfo> clumpInfo = new List<ClumpInfo>(); for (int offset = 1; offset < rawData.Length; ) { currentBestClump = FindBestClump(rawData, offset, maxReadBack, maxByteClump, minByteClump); if (currentBestClump.Length >= minByteClump) { //Set the clump, set back x bytes clumpInfo.Add(currentBestClump); offset += currentBestClump.Length; } else { //Uncompressed, move on one byte offset++; } } if (clumpInfo.Count == 0) { //Call the other one, huge waste of time return EncodeAsRaw(rawData); } //Optimization code for (int i = 0; i < clumpInfo.Count; i++) { if(FindBestAlternativeClumps(rawData, maxReadBack, maxByteClump, minByteClump, clumpInfo, i)) { //to try to further optimize? } } ClumpInfo nextCompressedSection = clumpInfo[0]; clumpInfo.RemoveAt(0); List<byte> UncompressedInfo = new List<byte>(); List<byte> CompressedInfo = new List<byte>(); List<byte> LayoutInfo = new List<byte>(); byte layoutByte = 0; int layoutByteIndex = 7; for (int offset = 0; offset < rawData.Length; ) { //If it's the offset from the next compressed section, do the compression. Else add an uncompressed. if (nextCompressedSection != null && offset == nextCompressedSection.Offset) { //Write the compressed data short compressedValues = (short)((((nextCompressedSection.Length - 3) & 0xF) << 12) | ((nextCompressedSection.CopyOffset - 1) & 0xFFF)); CompressedInfo.Add((byte)(compressedValues >> 8)); CompressedInfo.Add((byte)compressedValues); //No writing to the byte, since it's a 0 offset += nextCompressedSection.Length; if (clumpInfo.Count == 0) nextCompressedSection = null; else { nextCompressedSection = clumpInfo[0]; clumpInfo.RemoveAt(0); } } else { UncompressedInfo.Add(rawData[offset]); layoutByte |= (byte)(1 << layoutByteIndex); offset++; } layoutByteIndex--; if (layoutByteIndex < 0) { LayoutInfo.Add(layoutByte); layoutByte = 0; layoutByteIndex = 7; } } //Add the proper end buffers to the lists, put together the header, throw it all to the user here if (layoutByteIndex != 7) LayoutInfo.Add(layoutByte); while (LayoutInfo.Count % 4 != 0) LayoutInfo.Add(0); MIO0Header header = new MIO0Header(); header.ID = MIO0.MIO0_AS_UINT; header.OutputSize = (uint)rawData.Length; header.CompLoc = 0x10 + (uint)LayoutInfo.Count; header.RawLoc = header.CompLoc + (uint)CompressedInfo.Count; uint fullLength = header.RawLoc + (uint)UncompressedInfo.Count; //Pad if needed to hit a certain address value if (padToAddress != 0 && fullLength % padToAddress != 0) fullLength += padToAddress - (fullLength % padToAddress); byte[] finalData = new byte[fullLength]; header.WriteToBytes(finalData, 0); Array.Copy(LayoutInfo.ToArray(), 0, finalData, 0x10, LayoutInfo.Count); Array.Copy(CompressedInfo.ToArray(), 0, finalData, header.CompLoc, CompressedInfo.Count); Array.Copy(UncompressedInfo.ToArray(), 0, finalData, header.RawLoc, UncompressedInfo.Count); return finalData; }