Skip to content
Michael Williams edited this page Aug 2, 2014 · 11 revisions

##Yaz0

Yaz0 is a hybrid of RLE compression. (ShizZie says: "This is somewhat misleading. Run length encoding is only compression on a character-to-character basis, i.e. aaabbbbcccccc encodes to a2b3c5. Yaz0 compresses string-to-string, so it's more like LZMA."

You can see if you're dealing with the following compression if the first 4 bytes of the 16 byte long header contains Y a z 0. The second 4 bytes are in big-endian(uint32) and tells you how large the decompressed data is as well has give you a frame of reference on the size of your buffer. The following 8 bytes are always zero.

"First you read a "code" byte that tells you for the next 8 "read operations" what you have to do. Each bit of the "code" byte represents one "read operation" (from left to right, that is, 0x80 first, 0x01 last). If the bit is 1, copy one byte from the input buffer to the output buffer. Easy. If the bit is 0, things are a little bit more complicated, RLE compressed data is ahead. You have to read the next two bytes to decide how long your run is and what you should write to your output buffer.

15 8 7 0

a b

The upper nibble of the first byte (a) contains the information you need to determine how many bytes you're going to write to your output buffer for this "read operation". if a == 0, then you have to read a third byte from your input buffer, and add 0x12 to it. Otherwise, you simply add 2 to a. This is the number of bytes to write ("count") in this "read operation". byte2 and the lower nibble of byte1 (b) tell you from where to copy data to your output buffer: you move (dist = (b < <8) - byte2 + 1) bytes back in your outputBuffer and copy "count" bytes from thereto the end of the buffer. Note that count could be greater than dist which means that the copy source and copy destination might overlap." ~said it better than I could've.

###Decompression code

/*src points to the yaz0 source data (to the "real" source data, not at the header!) dst points to a buffer uncompressedSize bytes large (you get uncompressedSize from the second 4 bytes in the Yaz0 header). */

void decode(u8* src, u8* dst, int uncompressedSize) 
{ 
    int srcPlace = 0, dstPlace = 0; //current read/write positions 
    u32 validBitCount = 0; //number of valid bits left in "code" byte 
    u8 currCodeByte; 
    while(dstPlace < uncompressedSize) 
    { 
        //read new "code" byte if the current one is used up 
        if(validBitCount == 0) 
        { 
            currCodeByte = src[srcPlace]; 
            ++srcPlace; 
            validBitCount = 8; 
        } 
        if((currCodeByte & 0x80) != 0) 
        { 
            //straight copy 
            dst[dstPlace] = src[srcPlace]; 
            dstPlace++; 
            srcPlace++; 
        } 
        else 
        { 
            //RLE part 
            u8 byte1 = src[srcPlace]; 
            u8 byte2 = src[srcPlace + 1]; 
            srcPlace += 2; 
            u32 dist = ((byte1 & 0xF) < < 8) - byte2; 
            u32 copySource = dstPlace - (dist + 1); 
            u32 numBytes = byte1 > > 4;
            if (numBytes == 0)
            {
                numBytes = src[srcPlace] + 0x12;
                srcPlace++;
            }
            else
            {
                numBytes += 2;
            }

            //copy run 
            for(int i = 0; i < numBytes; ++i) 
            { 
                dst[dstPlace] = dst[copySource]; 
                copySource++; 
                dstPlace++; 
            }

             //use next bit from "code" byte 
             currCodeByte < <= 1; 
             validBitCount-=1;  
         } 
    }
}

##Yay0

This format is used to store the fonts in the BIOS/IPL. It is compressed similar to the the Zelda 64 'Yaz0' compression.

0x000 ;Yay0
0x0004 ;size of decoded data in bytes
0x0008 ;offset to link table
0x000c ;offset to non-linked chunks and count modifiers table
0x0010 ;packed data (32 bit words) with 0 being a linked chunk and 1 being non linked chunk

###Decompression code

private void Decode(void* s, void* d)
{
    u32 i, j, k;
    u32 p, q;
    u32 cnt;
    i = r21 = *(u32*) (s + 4); // size of decoded data 
    j = r29 = *(u32*) (s + 8); // link table 
    k = r23 = *(u32*) (s + 12); // byte chunks and count modifiers 
    q = r31 = 0; // current offset in dest buffer 
    cnt = r28 = 0; // mask bit counter 
    p = r24 = 16; // current offset in mask table 
    do
    {
        // if all bits are done, get next mask 
        if (cnt == 0)
        {
            // read word from mask data block 
            r22 = *(u32*) (s + p);
            p += 4;
            cnt = 32; // bit counter 
        }
        // if next bit is set, chunk is non-linked 
        if (r22 & 0x80000000)
        {
            // get next byte 
            *(u8*) (d + q) = *(u8*) (s + k);
            k++;
            q++;
        } 
            // do copy, otherwise 
        else
        {
            // read 16-bit from link table 
            r26 = *(u16*) (s + j);
            j += 2;
            // 'offset' 
            r25 = q - (r26 & 0xfff);
            // 'count' 
            r30 = r26 > >
            12;
            if (r30 == 0)
            {
                // get 'count' modifier 
                r5 = *(u8*) (s + k);
                k++;
                r30 = r5 + 18;
            }
            else r30 += 2;
            // do block copy 
            r5 = d + r25;
            for (i = 0; i < r30; i++)
            {
                *(u8*) (d + q) = *(u8*) (r5 - 1);
                q++;
                r5++;
            }
        }
        // next bit in mask 
        r22 < <=
        1;
        cnt--;
    } while (q < i);
}
Clone this wiki locally