z_jpeg, jpegutils and jpegdecoder OK (#322)

* this is a disaster

* two more

* func_800F470C

* split and rename jpegutils and jpegdecoder

* match jpegutils

* match jpegdecoder.c

* audio_rodata

* data split

* Split rsp

* Steal documentation from OoT

* cleanup

* Format

* remove removed members in JpegContext

* Update include/z64.h

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* Update src/code/jpegdecoder.c

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* Update include/functions.h

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* Update src/code/jpegutils.c

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>

* review renames

* true

* Some type fixes

* Update include/z64.h

Co-authored-by: EllipticEllipsis <73679967+EllipticEllipsis@users.noreply.github.com>

* add comment

* Update src/code/z_jpeg.c

Co-authored-by: EllipticEllipsis <73679967+EllipticEllipsis@users.noreply.github.com>

* format

* bss fix

* decr

* format

* z64jpeg.h

* Add stdbool

* Rename audio_init_params

* whoops

* whoops++

* whoops#

* remove extra dumb variables in variables.txt

* fix

Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
Co-authored-by: EllipticEllipsis <73679967+EllipticEllipsis@users.noreply.github.com>
This commit is contained in:
Anghelo Carvajal
2021-11-19 17:34:45 -03:00
committed by GitHub
parent c8b0ff3f74
commit 2854294009
15 changed files with 809 additions and 121 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
#include "global.h"
// Blocks the current thread until all currently queued scheduler tasks have been completed
void func_8010C1B0(void) {
void MsgEvent_SendNullTask(void) {
OSScTask task;
OSMesgQueue queue;
OSMesg msg;
-9
View File
@@ -1,9 +0,0 @@
#include "global.h"
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801AA020/func_801AA020.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801AA020/func_801AA248.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801AA020/func_801AA3E4.s")
#pragma GLOBAL_ASM("asm/non_matchings/code/code_801AA020/func_801AA520.s")
+193
View File
@@ -0,0 +1,193 @@
#include "z64jpeg.h"
#include "libc/stdbool.h"
#include "macros.h"
u8* sJpegBitStreamPtr;
u32 sJpegBitStreamByteIdx;
u8 sJpegBitStreamBitIdx;
u8 sJpegBitStreamDontSkip;
u32 sJpegBitStreamCurWord;
s32 JpegDecoder_Decode(JpegDecoder* decoder, u16* mcuBuff, s32 count, u8 isFollowing, JpegDecoderState* state) {
s16 pad;
s16 unk0;
s16 unk1;
s16 unk2;
u32 idx;
s32 inc;
u16 unkCount;
JpegHuffmanTable* hTable0;
JpegHuffmanTable* hTable1;
JpegHuffmanTable* hTable2;
JpegHuffmanTable* hTable3;
inc = 0;
sJpegBitStreamPtr = decoder->imageData;
if (decoder->mode == 0) {
unkCount = 2;
} else {
unkCount = 4;
if (decoder->unk_05 == 1) {
inc = 8 * 8 * 2;
}
}
hTable0 = decoder->hTablePtrs[0];
hTable1 = decoder->hTablePtrs[1];
hTable2 = decoder->hTablePtrs[2];
hTable3 = decoder->hTablePtrs[3];
if (!isFollowing) {
sJpegBitStreamByteIdx = 0;
sJpegBitStreamBitIdx = 32;
sJpegBitStreamCurWord = 0;
sJpegBitStreamDontSkip = 0;
unk0 = 0;
unk1 = 0;
unk2 = 0;
} else {
sJpegBitStreamByteIdx = state->byteIdx;
sJpegBitStreamBitIdx = state->bitIdx;
sJpegBitStreamCurWord = state->curWord;
sJpegBitStreamDontSkip = state->dontSkip;
unk0 = state->unk_0C;
unk1 = state->unk_0E;
unk2 = state->unk_10;
}
while (count != 0) {
for (idx = 0; idx < unkCount; idx++) {
if (JpegDecoder_ProcessMcu(hTable0, hTable1, mcuBuff, &unk0)) {
return 2;
}
mcuBuff += 8 * 8;
}
if (JpegDecoder_ProcessMcu(hTable2, hTable3, mcuBuff, &unk1)) {
return 2;
}
mcuBuff += 8 * 8;
if (JpegDecoder_ProcessMcu(hTable2, hTable3, mcuBuff, &unk2)) {
return 2;
}
count--;
mcuBuff += 8 * 8;
mcuBuff += inc;
}
state->byteIdx = sJpegBitStreamByteIdx;
state->bitIdx = sJpegBitStreamBitIdx;
state->curWord = sJpegBitStreamCurWord;
state->dontSkip = sJpegBitStreamDontSkip;
state->unk_0C = unk0;
state->unk_0E = unk1;
state->unk_10 = unk2;
return 0;
}
s32 JpegDecoder_ProcessMcu(JpegHuffmanTable* hTable0, JpegHuffmanTable* hTable1, u16* mcu, s16* unk) {
s8 i = 0;
s8 zeroCount;
s16 coeff;
if (JpegDecoder_ParseNextSymbol(hTable0, &coeff, &zeroCount)) {
return true;
}
*unk += coeff;
mcu[i++] = *unk;
while (i < 8 * 8) {
if (JpegDecoder_ParseNextSymbol(hTable1, &coeff, &zeroCount)) {
return true;
}
if (coeff == 0) {
if (zeroCount == 0xF) {
while (zeroCount-- >= 0) {
mcu[i++] = 0;
}
} else {
while (i < 8 * 8) {
mcu[i++] = 0;
}
break;
}
} else {
while (0 < zeroCount--) {
mcu[i++] = 0;
}
mcu[i++] = coeff;
}
}
return false;
}
s32 JpegDecoder_ParseNextSymbol(JpegHuffmanTable* hTable, s16* outCoeff, s8* outZeroCount) {
u8 codeIdx;
u8 sym;
u16 codeOff = 0;
u16 buff = JpegDecoder_ReadBits(16);
for (codeIdx = 0; codeIdx < ARRAY_COUNT(hTable->codesB); codeIdx++) {
if (hTable->codesB[codeIdx] == 0xFFFF) {
continue;
}
codeOff = buff >> (15 - codeIdx);
if (codeOff <= hTable->codesB[codeIdx]) {
break;
}
}
if (codeIdx >= ARRAY_COUNT(hTable->codesB)) {
return true;
}
sym = hTable->symbols[hTable->codeOffs[codeIdx] + codeOff - hTable->codesA[codeIdx]];
*outZeroCount = sym >> 4;
sym &= 0xF;
sJpegBitStreamBitIdx += codeIdx - 15;
*outCoeff = 0;
if (sym) {
*outCoeff = JpegDecoder_ReadBits(sym);
if (*outCoeff < (1 << (sym - 1))) {
*outCoeff += (-1 << sym) + 1;
}
}
return false;
}
u16 JpegDecoder_ReadBits(u8 len) {
u8 byteCount;
u8 data;
s32 ret;
u32 temp;
ret = 0; // this is required for some reason
for (byteCount = sJpegBitStreamBitIdx >> 3; byteCount > 0; byteCount--) {
data = sJpegBitStreamPtr[sJpegBitStreamByteIdx++];
if (sJpegBitStreamDontSkip) {
if (data == 0) {
data = sJpegBitStreamPtr[sJpegBitStreamByteIdx++];
}
}
sJpegBitStreamDontSkip = (data == 0xFF) ? true : false;
sJpegBitStreamCurWord <<= 8;
sJpegBitStreamCurWord |= data;
sJpegBitStreamBitIdx -= 8;
}
ret = (sJpegBitStreamCurWord << (sJpegBitStreamBitIdx));
temp = ret;
ret = temp >> -len;
sJpegBitStreamBitIdx += len;
return ret;
}
+147 -9
View File
@@ -1,17 +1,155 @@
#include "global.h"
#include "z64jpeg.h"
#include "libc/stdbool.h"
#include "macros.h"
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9B10.s")
void JpegUtils_ProcessQuantizationTable(u8* dqt, JpegQuantizationTable* qt, u8 count) {
u8 i;
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9B78.s")
for (i = 0; i < count; i++) {
u8 j;
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9BFC.s")
dqt++;
for (j = 0; j < ARRAY_COUNT(qt[i].table); j++) {
qt[i].table[j] = *dqt++;
}
}
}
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9C68.s")
s32 JpegUtils_ParseHuffmanCodesLengths(u8* ptr, u8* codesLengths) {
u8 off = 1;
s16 count = 0;
s16 idx = 1;
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9D10.s")
while (off <= 16) {
while (idx <= ptr[off - 1]) {
codesLengths[count++] = off;
idx++;
}
idx = 1;
off++;
}
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9DCC.s")
codesLengths[count] = 0;
return count;
}
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9EA4.s")
s32 JpegUtils_GetHuffmanCodes(u8* codesLengths, u16* codes) {
s16 idx = 0;
u16 code = 0;
u8 lastLen = codesLengths[0];
#pragma GLOBAL_ASM("asm/non_matchings/code/jpegutils/func_801A9F4C.s")
while (true) {
while (true) {
codes[idx++] = code++;
if (codesLengths[idx] != lastLen) {
break;
}
}
if (codesLengths[idx] == 0) {
break;
}
while (true) {
if (code <<= 1, codesLengths[idx] == ++lastLen) {
break;
}
}
}
return idx;
}
s32 JpegUtils_SetHuffmanTable(u8* data, JpegHuffmanTable* ht, u16* codes) {
u8 idx;
u16 codeOff = 0;
for (idx = 0; idx < 16; idx++) {
if (data[idx]) {
ht->codeOffs[idx] = codeOff;
ht->codesA[idx] = codes[codeOff];
codeOff += data[idx] - 1;
ht->codesB[idx] = codes[codeOff];
codeOff++;
} else {
ht->codesB[idx] = 0xFFFF;
}
}
return codeOff;
}
u32 JpegUtils_ProcessHuffmanTableImpl(u8* data, JpegHuffmanTable* ht, u8* codesLengths, u16* codes, u8 isAc) {
s16 ret;
s32 count = JpegUtils_ParseHuffmanCodesLengths(data, codesLengths);
s32 temp;
ret = count;
if (count == 0 || (isAc && count > 0x100) || (!isAc && count > 0x10)) {
return 0;
}
if (ret != JpegUtils_GetHuffmanCodes(codesLengths, codes)) {
return 0;
}
if (temp = JpegUtils_SetHuffmanTable(data, ht, codes), temp != ret) {
return 0;
}
return ret;
}
u32 JpegUtils_ProcessHuffmanTable(u8* dht, JpegHuffmanTable* ht, u8* codesLengths, u16* codes, u8 count) {
u8 idx;
u32 codeCount;
for (idx = 0; idx < count; idx++) {
u32 ac = (*dht++ >> 4);
codeCount = JpegUtils_ProcessHuffmanTableImpl(dht, &ht[idx], codesLengths, codes, ac);
if (codeCount == 0) {
return true;
}
dht += 16;
ht[idx].symbols = dht;
dht += codeCount;
}
return false;
}
void JpegUtils_SetHuffmanTableOld(u8* data, JpegHuffmanTableOld* ht, u8* codesLengths, u16* codes, s16 count, u8 isAc) {
s16 idx;
u8 a;
for (idx = 0; idx < count; idx++) {
a = data[idx];
if (isAc) {
ht->acCodes[a] = codes[idx];
ht->codeOffs[a] = codesLengths[idx];
} else {
ht->dcCodes[a] = codes[idx];
ht->codeOffs[a] = codesLengths[idx];
}
}
}
u32 JpegUtils_ProcessHuffmanTableImplOld(u8* dht, JpegHuffmanTableOld* ht, u8* codesLengths, u16* codes) {
u8 isAc = *dht++ >> 4;
s16 count2;
s32 count;
count2 = count = JpegUtils_ParseHuffmanCodesLengths(dht, codesLengths);
if (count == 0 || (isAc && count > 0x100) || (!isAc && count > 0x10)) {
return true;
}
if (JpegUtils_GetHuffmanCodes(codesLengths, codes) != count2) {
return true;
}
JpegUtils_SetHuffmanTableOld(dht + 0x10, ht, codesLengths, codes, count2, isAc);
return false;
}
+299 -6
View File
@@ -1,11 +1,304 @@
#include "global.h"
#include "z64jpeg.h"
#include "libc/stdbool.h"
#include "variables.h"
#include "functions.h"
#pragma GLOBAL_ASM("asm/non_matchings/code/z_jpeg/func_800F42A0.s")
#define MARKER_ESCAPE 0x00
#define MARKER_SOI 0xD8
#define MARKER_SOF 0xC0
#define MARKER_DHT 0xC4
#define MARKER_DQT 0xDB
#define MARKER_DRI 0xDD
#define MARKER_SOS 0xDA
#define MARKER_APP0 0xE0
#define MARKER_APP1 0xE1
#define MARKER_APP2 0xE2
#define MARKER_COM 0xFE
#define MARKER_EOI 0xD9
#pragma GLOBAL_ASM("asm/non_matchings/code/z_jpeg/func_800F43BC.s")
/**
* Configures and schedules a JPEG decoder task and waits for it to finish.
*/
void Jpeg_ScheduleDecoderTask(JpegContext* jpegCtx) {
static OSTask_t sJpegTask = {
M_NJPEGTASK, // type
0, // flags
NULL, // ucode_boot
0, // ucode_boot_size
gJpegUCode, // ucode
0x1000, // ucode_size
gJpegUCodeData, // ucode_data
0x800, // ucode_data_size
NULL, // dram_stack
0, // dram_stack_size
NULL, // output_buff
NULL, // output_buff_size
NULL, // data_ptr
sizeof(JpegTaskData), // data_size
NULL, // yield_data_ptr
0x200, // yield_data_size
};
#pragma GLOBAL_ASM("asm/non_matchings/code/z_jpeg/func_800F44F4.s")
JpegWork* workBuf = jpegCtx->workBuf;
s32 pad[2];
#pragma GLOBAL_ASM("asm/non_matchings/code/z_jpeg/func_800F4540.s")
workBuf->taskData.address = &workBuf->data;
workBuf->taskData.mode = jpegCtx->mode;
workBuf->taskData.mbCount = 4;
workBuf->taskData.qTableYPtr = &workBuf->qTableY;
workBuf->taskData.qTableUPtr = &workBuf->qTableU;
workBuf->taskData.qTableVPtr = &workBuf->qTableV;
#pragma GLOBAL_ASM("asm/non_matchings/code/z_jpeg/func_800F470C.s")
sJpegTask.flags = 0;
sJpegTask.ucodeBoot = SysUcode_GetUCodeBoot();
sJpegTask.ucodeBootSize = SysUcode_GetUCodeBootSize();
sJpegTask.yieldDataPtr = (u64*)&workBuf->yieldData;
sJpegTask.dataPtr = (u64*)&workBuf->taskData;
jpegCtx->scTask.next = NULL;
jpegCtx->scTask.flags = OS_SC_NEEDS_RSP;
jpegCtx->scTask.msgQ = &jpegCtx->mq;
jpegCtx->scTask.msg = NULL;
jpegCtx->scTask.framebuffer = NULL;
jpegCtx->scTask.list.t = sJpegTask;
osSendMesg(&gSchedContext.cmdQ, (OSMesg)&jpegCtx->scTask, OS_MESG_BLOCK);
Sched_SendEntryMsg(&gSchedContext); // osScKickEntryMsg
osRecvMesg(&jpegCtx->mq, NULL, OS_MESG_BLOCK);
}
/**
* Copies a 16x16 block of decoded image data to the Z-buffer.
*/
void Jpeg_CopyToZbuffer(u16* src, u16* zbuffer, s32 x, s32 y) {
u16* dst = zbuffer + (((y * SCREEN_WIDTH) + x) * 16);
s32 i;
for (i = 0; i < 16; i++) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
dst[4] = src[4];
dst[5] = src[5];
dst[6] = src[6];
dst[7] = src[7];
dst[8] = src[8];
dst[9] = src[9];
dst[10] = src[10];
dst[11] = src[11];
dst[12] = src[12];
dst[13] = src[13];
dst[14] = src[14];
dst[15] = src[15];
src += 16;
dst += SCREEN_WIDTH;
}
}
/**
* Reads an u16 from a possibly unaligned address in memory.
*
* Replaces unaligned 16-bit reads with a pair of aligned reads, allowing for reading the possibly
* unaligned values in JPEG header files.
*/
u16 Jpeg_GetUnalignedU16(u8* ptr) {
if (((u32)ptr & 1) == 0) {
// Read the value normally if it's aligned to a 16-bit address.
return *(u16*)ptr;
} else {
// Read unaligned values using two separate aligned memory accesses when it's not.
return *(u16*)(ptr - 1) << 8 | (*(u16*)(ptr + 1) >> 8);
}
}
/**
* Parses the markers in the JPEG file, storing information such as the pointer to the image data
* in `jpegCtx` for later processing.
*/
void Jpeg_ParseMarkers(u8* ptr, JpegContext* jpegCtx) {
u32 exit = false;
jpegCtx->dqtCount = 0;
jpegCtx->dhtCount = 0;
while (true) {
if (exit) {
break;
}
// 0xFF indicates the start of a JPEG marker, so look for the next.
if (*ptr++ == 0xFF) {
switch (*ptr++) {
case MARKER_ESCAPE: {
// Compressed value 0xFF is stored as 0xFF00 to escape it, so ignore it.
break;
}
case MARKER_SOI: {
// Start of Image
break;
}
case MARKER_APP0: {
// Application marker for JFIF
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_APP1: {
// Application marker for EXIF
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_APP2: {
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_DQT: {
// Define Quantization Table, stored for later processing
jpegCtx->dqtPtr[jpegCtx->dqtCount++] = ptr + 2;
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_DHT: {
// Define Huffman Table, stored for later processing
jpegCtx->dhtPtr[jpegCtx->dhtCount++] = ptr + 2;
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_DRI: {
// Define Restart Interval
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_SOF: {
// Start of Frame, stores important metadata of the image.
// Only used for extracting the sampling factors (jpegCtx->mode).
if (ptr[9] == 0x21) {
// component Y : V0 == 1
jpegCtx->mode = 0;
} else if (ptr[9] == 0x22) {
// component Y : V0 == 2
jpegCtx->mode = 2;
}
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
case MARKER_SOS: {
// Start of Scan marker, indicates the start of the image data.
ptr += Jpeg_GetUnalignedU16(ptr);
jpegCtx->imageData = ptr;
break;
}
case MARKER_EOI: {
// End of Image
exit = true;
break;
}
default: {
ptr += Jpeg_GetUnalignedU16(ptr);
break;
}
}
}
}
}
s32 Jpeg_Decode(void* data, void* zbuffer, void* work, u32 workSize) {
s32 y;
s32 x;
s32 j;
s32 i;
JpegContext jpegCtx;
JpegHuffmanTable hTables[4];
JpegDecoder decoder;
JpegDecoderState state;
JpegWork* workBuff = work;
if (workSize < sizeof(JpegWork)) {
return -1;
}
osCreateMesgQueue(&jpegCtx.mq, &jpegCtx.msg, 1);
MsgEvent_SendNullTask();
jpegCtx.workBuf = workBuff;
Jpeg_ParseMarkers(data, &jpegCtx);
switch (jpegCtx.dqtCount) {
case 1:
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[0], &workBuff->qTableY, 3);
break;
case 2:
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[0], &workBuff->qTableY, 1);
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[1], &workBuff->qTableU, 1);
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[1], &workBuff->qTableV, 1);
break;
case 3:
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[0], &workBuff->qTableY, 1);
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[1], &workBuff->qTableU, 1);
JpegUtils_ProcessQuantizationTable(jpegCtx.dqtPtr[2], &workBuff->qTableV, 1);
break;
default:
return -1;
}
switch (jpegCtx.dhtCount) {
case 1:
if (JpegUtils_ProcessHuffmanTable(jpegCtx.dhtPtr[0], &hTables[0], workBuff->codesLengths, workBuff->codes,
4)) {
return -1;
}
break;
case 4:
// This chained if-else has printfs inside it on debug
if (JpegUtils_ProcessHuffmanTable(jpegCtx.dhtPtr[0], &hTables[0], workBuff->codesLengths, workBuff->codes,
1)) {
} else if (JpegUtils_ProcessHuffmanTable(jpegCtx.dhtPtr[1], &hTables[1], workBuff->codesLengths,
workBuff->codes, 1)) {
} else if (JpegUtils_ProcessHuffmanTable(jpegCtx.dhtPtr[2], &hTables[2], workBuff->codesLengths,
workBuff->codes, 1)) {
} else if (!JpegUtils_ProcessHuffmanTable(jpegCtx.dhtPtr[3], &hTables[3], workBuff->codesLengths,
workBuff->codes, 1)) {
break;
}
return -1;
default:
return -1;
}
decoder.imageData = jpegCtx.imageData;
decoder.mode = jpegCtx.mode;
decoder.unk_05 = 2;
decoder.hTablePtrs[0] = &hTables[0];
decoder.hTablePtrs[1] = &hTables[1];
decoder.hTablePtrs[2] = &hTables[2];
decoder.hTablePtrs[3] = &hTables[3];
decoder.unk_18 = 0;
x = y = 0;
for (i = 0; i < 300; i += 4) {
if (!JpegDecoder_Decode(&decoder, (u16*)workBuff->data, 4, (i != 0), &state)) {
Jpeg_ScheduleDecoderTask(&jpegCtx);
for (j = 0; j < 4; j++) {
Jpeg_CopyToZbuffer(workBuff->data[j], zbuffer, x, y);
x++;
if (x >= 20) {
x = 0;
y++;
}
}
}
}
return 0;
}