/// <summary> /// a lookahead encoding scheme for ngc Yaz0 /// </summary> /// <param name="src"></param> /// <param name="size"></param> /// <param name="pos"></param> /// <param name="pMatchPos"></param> /// <returns></returns> static UInt32 nintendoEnc(byte[] src, int size, int pos, ref uint pMatchPos, StaticEncodeVars var) //u32 nintendoEnc(u8* src, int size, int pos, u32 *pMatchPos) { //int startPos = pos - 0x1000; uint numBytes = 1; // if prevFlag is set, it means that the previous position was determined by look-ahead try. // so just use it. this is not the best optimization, but nintendo's choice for speed. if (var.prevFlag == 1) { pMatchPos = var.matchPos; //*pMatchPos = matchPos; var.prevFlag = 0; return(var.numBytes1); } var.prevFlag = 0; numBytes = simpleEnc(src, size, pos, ref var.matchPos); //numBytes = simpleEnc(src, size, pos, &matchPos); pMatchPos = var.matchPos; //*pMatchPos = matchPos; // if this position is RLE encoded, then compare to copying 1 byte and next position(pos+1) encoding if (numBytes >= 3) { var.numBytes1 = simpleEnc(src, size, pos + 1, ref var.matchPos); //numBytes1 = simpleEnc(src, size, pos+1, &matchPos); // if the next position encoding is +2 longer than current position, choose it. // this does not guarantee the best optimization, but fairly good optimization with speed. if (var.numBytes1 >= numBytes + 2) { numBytes = 1; var.prevFlag = 1; } } return(numBytes); }
/// <summary> /// Writes compressed file to given stream, starting at stream's position. /// </summary> /// <param name="src"></param> /// <param name="srcSize"></param> /// <param name="dstFile"></param> /// <returns></returns> public static int Encode(byte[] src, int srcSize, Stream dstFile) { Ret r = new Ret(0, 0); byte[] dst = new byte[24]; // 8 codes * 3 bytes maximum int dstSize = 0; uint validBitCount = 0; //number of valid bits left in "code" byte byte currCodeByte = 0; uint numBytes; uint matchPos = 0; StaticEncodeVars var = new StaticEncodeVars(); //Write Header byte[] srcSizeArr = BitConverter.GetBytes(srcSize); byte[] header = new byte[] { 0x59, 0x61, 0x7A, 0x30 }; //Yaz0 if (BitConverter.IsLittleEndian) { Array.Reverse(srcSizeArr); } dstFile.Write(header, 0, 4); dstFile.Write(srcSizeArr, 0, 4); for (int i = 0; i < 8; i++) { dstFile.WriteByte(0); } while (r.srcPos < srcSize) { numBytes = nintendoEnc(src, srcSize, r.srcPos, ref matchPos, var); //matchPos passed ref &matchpos if (numBytes < 3) { //straight copy dst[r.dstPos] = src[r.srcPos]; r.dstPos++; r.srcPos++; //set flag for straight copy currCodeByte |= (byte)(0x80 >> (int)validBitCount); } else { //RLE part UInt32 dist = (UInt32)r.srcPos - matchPos - 1; byte byte1, byte2, byte3; if (numBytes >= 0x12) // 3 byte encoding { byte1 = (byte)(0 | (dist >> 8)); byte2 = (byte)(dist & 0xff); dst[r.dstPos++] = byte1; dst[r.dstPos++] = byte2; // maximum runlength for 3 byte encoding if (numBytes > 0xff + 0x12) { numBytes = 0xff + 0x12; } byte3 = (byte)(numBytes - 0x12); dst[r.dstPos++] = byte3; } else // 2 byte encoding { byte1 = (byte)(((numBytes - 2) << 4) | (dist >> 8)); byte2 = (byte)(dist & 0xff); dst[r.dstPos++] = byte1; dst[r.dstPos++] = byte2; } r.srcPos += (int)numBytes; } validBitCount++; //write eight codes if (validBitCount == 8) { dstFile.WriteByte(currCodeByte); dstFile.Write(dst, 0, r.dstPos); dstSize += r.dstPos + 1; currCodeByte = 0; validBitCount = 0; r.dstPos = 0; } } if (validBitCount > 0) { dstFile.WriteByte(currCodeByte); dstFile.Write(dst, 0, r.dstPos); dstSize += r.dstPos + 1; currCodeByte = 0; validBitCount = 0; r.dstPos = 0; } return(dstSize); }