//
// Generated by dtk
// Translation Unit: JKRAramArchive.cpp
//

#include "JSystem/JSystem.h" // IWYU pragma: keep

#include "JSystem/JKernel/JKRAramArchive.h"
#include "JSystem/JKernel/JKRAram.h"
#include "JSystem/JKernel/JKRAramBlock.h"
#include "JSystem/JKernel/JKRAramHeap.h"
#include "JSystem/JKernel/JKRDecomp.h"
#include "JSystem/JKernel/JKRDvdFile.h"
#include "JSystem/JKernel/JKRDvdRipper.h"
#include "JSystem/JKernel/JKRDvdAramRipper.h"
#include "JSystem/JKernel/JKRHeap.h"
#include "JSystem/JUtility/JUTAssert.h"
#include "math.h"
#include "string.h"
#include "dolphin/os/OS.h"

/* 802B9FE0-802BA088       .text __ct__14JKRAramArchiveFlQ210JKRArchive15EMountDirection */
JKRAramArchive::JKRAramArchive(s32 entryNumber, JKRArchive::EMountDirection mountDirection) : JKRArchive(entryNumber, MOUNT_ARAM) {
    mMountDirection = mountDirection;

    if (!this->open(entryNumber)) {
        return;
    }

    mVolumeType = 'RARC';
    mVolumeName = mStringTable + mNodes->name_offset;
    JKRFileLoader::sVolumeList.prepend(&mFileLoaderLink);
    mIsMounted = true;
}

/* 802BA088-802BA1C8       .text __dt__14JKRAramArchiveFv */
JKRAramArchive::~JKRAramArchive() {
    if (mIsMounted == true) {
        if (mArcInfoBlock != NULL) {
            SDIFileEntry* entry = mFiles;
            for (int i = 0; i < mArcInfoBlock->num_file_entries; entry++, i++) {
                if (entry->data != NULL) {
                    JKRFreeToHeap(mHeap, entry->data);
                }
            }

            JKRFreeToHeap(mHeap, mArcInfoBlock);
            mArcInfoBlock = NULL;
        }

        if (mExpandedSize != NULL) {
            JKRFree(mExpandedSize);
            mExpandedSize = NULL;
        }

        if (mDvdFile != NULL) {
            delete mDvdFile;
        }

        if (mBlock != NULL) {
            delete mBlock;
        }

        JKRFileLoader::sVolumeList.remove(&mFileLoaderLink);
        mIsMounted = false;
    }
}

static void dummy() {
    OSReport(__FILE__);
    OSReport("isMounted()");
    OSReport("Halt");
    OSReport("mMountCount == 1");
}

/* 802BA1C8-802BA4EC       .text open__14JKRAramArchiveFl */
bool JKRAramArchive::open(s32 entryNum) {
    mArcInfoBlock = NULL;
    mNodes = NULL;
    mFiles = NULL;
    mStringTable = NULL;
    mBlock = NULL;

    mDvdFile = new (JKRGetSystemHeap(), mMountDirection == MOUNT_DIRECTION_HEAD ? 4 : -4) JKRDvdFile(entryNum);
    if (mDvdFile == NULL) {
        mMountMode = 0;
        return 0;
    }

    // NOTE: a different struct is used here for sure, unfortunately i can't get any hits on this address, so gonna leave it like this for now
    SArcHeader *mem = (SArcHeader *)JKRAllocFromSysHeap(32, -32);
    if (mem == NULL)  {
        mMountMode = 0;
    } else {
        JKRDvdToMainRam(entryNum, (u8 *)mem, EXPAND_SWITCH_UNKNOWN1, 32, NULL, JKRDvdRipper::ALLOC_DIRECTION_FORWARD, 0, &mCompression);
        int alignment = mMountDirection == MOUNT_DIRECTION_HEAD ? 32 : -32;
        u32 alignedSize = ALIGN_NEXT(mem->file_data_offset, 32);
        mArcInfoBlock = (SArcDataInfo *)JKRAllocFromHeap(mHeap, alignedSize, alignment);
        if (mArcInfoBlock == NULL) {
            mMountMode = 0;
        } else {
            JKRDvdToMainRam(entryNum, (u8 *)mArcInfoBlock, EXPAND_SWITCH_UNKNOWN1, alignedSize, NULL, JKRDvdRipper::ALLOC_DIRECTION_FORWARD, 32, NULL);

            mNodes = (SDIDirEntry *)((u8 *)mArcInfoBlock + mArcInfoBlock->node_offset);
            mFiles = (SDIFileEntry *)((u8 *)mArcInfoBlock + mArcInfoBlock->file_entry_offset);
            mStringTable = (char *)((u8 *)mArcInfoBlock + mArcInfoBlock->string_table_offset);
            mExpandedSize = NULL;

            u8 compressedFiles = 0; // maybe a check for if the last file is compressed?

            SDIFileEntry *fileEntry = mFiles;
            for (int i = 0; i < mArcInfoBlock->num_file_entries; i++) {
                u8 flag = fileEntry->type_flags_and_name_offset >> 24;
                if ((flag & 1))
                {
                    compressedFiles |= (flag & JKRARCHIVE_ATTR_COMPRESSION);
                }
                fileEntry++;
            }

            if (compressedFiles != 0) {
                mExpandedSize = (s32 *)JKRAllocFromHeap(mHeap, mArcInfoBlock->num_file_entries << 2, abs(alignment));
                if (mExpandedSize == NULL) {
                    JKRFree(mArcInfoBlock);
                    mMountMode = 0;
                    goto cleanup;
                }
                memset(mExpandedSize, 0, mArcInfoBlock->num_file_entries << 2);
            }

            u32 aramSize = ALIGN_NEXT(mem->file_data_length, 32);
            mBlock = (JKRAramBlock*) JKRAllocFromAram(aramSize, mMountDirection == MOUNT_DIRECTION_HEAD ? JKRAramHeap::HEAD : JKRAramHeap::TAIL);
            if(mBlock == NULL) {
                if (mArcInfoBlock) {
                    JKRFree(mArcInfoBlock);
                }
                if(mExpandedSize) {
                    JKRFree(mExpandedSize);
                }
                mMountMode = 0;
            }
            else {
                JKRDvdToAram(entryNum, mBlock->getAddress(), EXPAND_SWITCH_UNKNOWN1, mem->header_length + mem->file_data_offset, 0);
            }
        }
    }
cleanup:
    if (mem != NULL) {
        JKRFreeToSysHeap(mem);
    }
    if (mMountMode == 0) {
        OSReport(":::[%s: %d] Cannot alloc memory\n", __FILE__, VERSION_SELECT(407, 407, 400, 400));
        if (mDvdFile != NULL) {
            delete mDvdFile;
        }
        return false;
    }
    return true;
}

/* 802BA4EC-802BA640       .text fetchResource__14JKRAramArchiveFPQ210JKRArchive12SDIFileEntryPUl */
void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) {
    JUT_ASSERT(VERSION_SELECT(434, 434, 427, 427), isMounted());
    u32 outSize;
    u8* outBuf;
    if (pOutSize == NULL) {
        pOutSize = &outSize;
    }

    JKRCompression compression = JKRConvertAttrToCompressionType(pEntry->getFlags());
    if (pEntry->data == NULL) {
        u32 size = JKRAramArchive::fetchResource_subroutine(
            pEntry->data_offset + mBlock->getAddress(), pEntry->data_size, mHeap, compression,
            &outBuf);

        *pOutSize = size;
        if (size == NULL) {
            return NULL;
        }

        pEntry->data = outBuf;
        if (compression == COMPRESSION_YAZ0) {
            this->setExpandSize(pEntry, *pOutSize);
        }
    } else {
        if (compression == COMPRESSION_YAZ0) {
            *pOutSize = this->getExpandSize(pEntry);
        } else {
            *pOutSize = pEntry->data_size;
        }
    }

    return pEntry->data;
}

/* 802BA640-802BA788       .text fetchResource__14JKRAramArchiveFPvUlPQ210JKRArchive12SDIFileEntryPUl */
void* JKRAramArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* pEntry, u32* resourceSize) {
    JUT_ASSERT(VERSION_SELECT(514, 514, 500, 500), isMounted());
    u32 size = pEntry->data_size;
    if (size > bufferSize) {
        size = bufferSize;
    }
    JKRCompression compression = JKRConvertAttrToCompressionType(pEntry->getFlags());
    if (pEntry->data == NULL) {
        bufferSize = (s32)ALIGN_PREV(bufferSize, 0x20);
        size = JKRAramArchive::fetchResource_subroutine(pEntry->data_offset + mBlock->getAddress(),
                                                        size, (u8*)buffer, bufferSize, compression);
    } else {
        if (compression == COMPRESSION_YAZ0) {
            u32 expandSize = this->getExpandSize(pEntry);
            if (expandSize != NULL) {
                size = expandSize;
            }
        }
        if (size > bufferSize) {
            size = bufferSize;
        }
        JKRHeap::copyMemory(buffer, pEntry->data, size);
    }
    if (resourceSize != NULL) {
        *resourceSize = size;
    }
    return buffer;
}

/* 802BA788-802BA894       .text fetchResource_subroutine__14JKRAramArchiveFUlUlPUcUli */
u32 JKRAramArchive::fetchResource_subroutine(u32 srcAram, u32 srcLength, u8* dst, u32 dstLength, int compression) {
    JUT_ASSERT(VERSION_SELECT(627, 627, 613, 613), ( srcAram & 0x1f ) == 0);
    u32 outLen;
    u32 srcSize = ALIGN_NEXT(srcLength, 0x20);
    u32 dstSize = ALIGN_PREV(dstLength, 0x20);

    switch (compression) {
    case COMPRESSION_NONE:
        if (srcSize > dstSize) {
            srcSize = dstSize;
        }
        JKRAramToMainRam(srcAram, dst, srcSize, EXPAND_SWITCH_UNKNOWN0, dstSize, NULL, -1, &outLen);
        return outLen;
    case COMPRESSION_YAY0:
    case COMPRESSION_YAZ0:
        JKRAramToMainRam(srcAram, dst, srcSize, EXPAND_SWITCH_UNKNOWN1, dstSize, NULL, -1, &outLen);
        return outLen;
    default:
        OSPanic(__FILE__, VERSION_SELECT(654, 654, 640, 640), ":::??? bad sequence\n");
        return 0;
    }
}

/* 802BA894-802BAA48       .text fetchResource_subroutine__14JKRAramArchiveFUlUlP7JKRHeapiPPUc */
u32 JKRAramArchive::fetchResource_subroutine(u32 entryNum, u32 length, JKRHeap* pHeap, int compression, u8** out) {
    u32 alignedLen = ALIGN_NEXT(length, 0x20);

    u8* buffer;
    switch (compression) {
    case COMPRESSION_NONE:
        {
            buffer = (u8*)(JKRAllocFromHeap(pHeap, alignedLen, 0x20));
            JUT_ASSERT(VERSION_SELECT(676, 676, 662, 662), buffer != NULL);
            JKRAramToMainRam(entryNum, buffer, alignedLen, EXPAND_SWITCH_UNKNOWN0, alignedLen, NULL, -1, NULL);
            *out = buffer;
            return length;
        }
    case COMPRESSION_YAY0:
    case COMPRESSION_YAZ0:
        {
            u8 headerBuf[0x40];
            u8* alignHeader = (u8*)ALIGN_NEXT((s32)&headerBuf[0], sizeof(SArcHeader));
            JKRAramToMainRam(entryNum, alignHeader, sizeof(SArcHeader), EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL);
            u32 decompressedLen = ALIGN_NEXT(JKRDecompExpandSize(alignHeader), sizeof(SArcHeader));
            buffer = (u8*)(JKRAllocFromHeap(pHeap, decompressedLen, sizeof(SArcHeader)));
            JUT_ASSERT(VERSION_SELECT(702, 709, 688, 688), buffer);
            u32 readLen;
            JKRAramToMainRam(entryNum, buffer, alignedLen, EXPAND_SWITCH_UNKNOWN1, decompressedLen, pHeap, -1, &readLen);
            *out = buffer;
            return readLen;
        }
    default:
        OSPanic(__FILE__, VERSION_SELECT(712, 719, 698, 698), ":::??? bad sequence\n");
        return 0;
    }
}

/* 802BAA48-802BAB74       .text getExpandedResSize__14JKRAramArchiveCFPCv */
u32 JKRAramArchive::getExpandedResSize(const void* ptr) const {
    if (mExpandedSize == NULL) {
        return this->getResSize(ptr);
    }

    JKRArchive::SDIFileEntry* entry = this->findPtrResource(ptr);
    if (entry == NULL) {
        return 0xFFFFFFFF;
    }

    if (entry->getCompressFlag() == 0) {
        return this->getResSize(ptr);
    }

    u32 expandSize = this->getExpandSize(entry);
    if (expandSize != 0) {
        return expandSize;
    }

    u8 tmpBuf[0x40];
    u8* buf = (u8*)ALIGN_PREV((s32)&tmpBuf[0x1F], 0x20);
    JKRAramToMainRam(entry->data_offset + mBlock->getAddress(), buf, 0x20,
                     EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL);
    expandSize = JKRDecompExpandSize(buf);
    // ??? casting away const?
    ((JKRArchive*)this)->setExpandSize(entry, expandSize);
    return expandSize;
}

/* 802BAB74-802BABD4       .text __dt__7JKRFileFv */
//JKRFile::~JKRFile() {}
