summaryrefslogtreecommitdiff
path: root/src/boot/yaz0.c
blob: 7badc69a2cc0f5aa72f577473835003d85c1c603 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include "yaz0.h"
#include "libc64/sleep.h"
#include "fault.h"
#include "m_std_dma.h"

u8 sYaz0DataBuffer[0x400];
u8* sYaz0CurDataEnd;
uintptr_t sYaz0CurRomStart;
size_t sYaz0CurSize;
u8* sYaz0MaxPtr;
void* gYaz0DecompressDstEnd;

void* Yaz0_FirstDMA(void) {
    UNUSED s32 pad;
    size_t dmaSize;
    size_t curSize;

    sYaz0MaxPtr = sYaz0CurDataEnd - 25;
    curSize = sYaz0CurDataEnd - sYaz0DataBuffer;
    dmaSize = (sYaz0CurSize < curSize) ? sYaz0CurSize : curSize;
    DmaMgr_DmaRomToRam(sYaz0CurRomStart, sYaz0DataBuffer, dmaSize);
    sYaz0CurRomStart += dmaSize;
    sYaz0CurSize -= dmaSize;

    return sYaz0DataBuffer;
}

void* Yaz0_NextDMA(void* curSrcPos) {
    u8* dst;
    size_t restSize;
    size_t dmaSize;
    OSPri oldPri;

    restSize = (uintptr_t)sYaz0CurDataEnd - (uintptr_t)curSrcPos;

    dst = (restSize & 7) ? (sYaz0DataBuffer - (restSize & 7)) + 8 : sYaz0DataBuffer;

    bcopy(curSrcPos, dst, restSize);
    dmaSize = (sYaz0CurDataEnd - dst) - restSize;
    if (sYaz0CurSize < dmaSize) {
        dmaSize = sYaz0CurSize;
    }

    if (dmaSize != 0) {
        DmaMgr_DmaRomToRam(sYaz0CurRomStart, dst + restSize, dmaSize);
        sYaz0CurRomStart += dmaSize;
        sYaz0CurSize -= dmaSize;
        if (sYaz0CurSize == 0) {
            sYaz0MaxPtr = dst + restSize + dmaSize;
        }
    } else {
        oldPri = osGetThreadPri(NULL);
        osSetThreadPri(NULL, OS_PRIORITY_APPMAX);
        // "decompression abnormality"
        osSyncPrintf("圧縮展開異常\n");
        osSetThreadPri(NULL, oldPri);
    }

    return dst;
}

typedef struct {
    /* 0x0 */ u32 magic; // Yaz0
    /* 0x4 */ u32 decSize;
    /* 0x8 */ u32 compInfoOffset;   // only used in mio0
    /* 0xC */ u32 uncompDataOffset; // only used in mio0
} Yaz0Header;                       // size = 0x10

#define YAZ0_MAGIC 0x59617A30 // "Yaz0"

s32 Yaz0_DecompressImpl(u8* src, u8* dst) {
    u32 bitIdx = 0;
    u8* dstEnd;
    u32 chunkHeader = 0;
    u32 nibble;
    u8* backPtr;
    s32 chunkSize;
    u32 off;
    u32 magic = ((Yaz0Header*)src)->magic;

    if (magic != YAZ0_MAGIC) {
        return -1;
    }

    dstEnd = dst + ((Yaz0Header*)src)->decSize;
    src = src + sizeof(Yaz0Header);

    do {
        if (bitIdx == 0) {
            if ((sYaz0MaxPtr < src) && (sYaz0CurSize != 0)) {
                src = Yaz0_NextDMA(src);
            }

            chunkHeader = *src++;
            bitIdx = 8;
        }

        if (chunkHeader & (1 << 7)) { // uncompressed
            *dst = *src;
            dst++;
            src++;
        } else { // compressed
            off = ((*src & 0xF) << 8 | *(src + 1));
            nibble = *src >> 4;
            backPtr = dst - off;
            src += 2;

            chunkSize = (nibble == 0)              // N = chunkSize; B = back offset
                            ? (u32)(*src++ + 0x12) // 3 bytes 0B BB NN
                            : nibble + 2;          // 2 bytes NB BB

            do {
                *dst++ = *(backPtr++ - 1);
                chunkSize--;
            } while (chunkSize != 0);
        }
        chunkHeader <<= 1;
        bitIdx--;
    } while (dst != dstEnd);

    gYaz0DecompressDstEnd = dstEnd;

    return 0;
}

void Yaz0_Decompress(RomOffset romStart, void* dst, size_t size) {
    s32 status;
    UNUSED s32 pad;
    char exp1[80];
    char exp2[80];

    if (sYaz0CurDataEnd != NULL) {
        while (sYaz0CurDataEnd != NULL) {
            usleep(10);
        }
    }

    sYaz0CurDataEnd = sYaz0DataBuffer + sizeof(sYaz0DataBuffer);
    sYaz0CurRomStart = romStart;
    sYaz0CurSize = size;
    status = Yaz0_DecompressImpl(Yaz0_FirstDMA(), dst);

    if (status != 0) {
        sprintf(exp1, "slidma slidstart_szs ret=%d", status);
        sprintf(exp2, "src:%08lx dst:%08lx siz:%08lx", romStart, dst, size);
        fault_AddHungupAndCrashImpl(exp1, exp2);
    }

    sYaz0CurDataEnd = NULL;
}