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

#include "JSystem/JKernel/JKRExpHeap.h"
#include "JSystem/JSupport/JSupport.h"
#include "JSystem/JUtility/JUTAssert.h"
#include "JSystem/JUtility/JUTConsole.h"
#include "JSystem/JUtility/JUTException.h"
#include "dolphin/os/OS.h"
#include "dolphin/types.h"

/* 802B1558-802B15D0       .text createRoot__10JKRExpHeapFib */
JKRExpHeap* JKRExpHeap::createRoot(int maxHeaps, bool errorFlag) {
    JKRExpHeap* heap = NULL;
    if (!sRootHeap) {
        void* memory;
        u32 memorySize;
        initArena((char**)&memory, &memorySize, maxHeaps);
        u8* start = (u8*)memory + OSRoundUp(sizeof(JKRExpHeap), 0x10);
        u32 alignedSize = memorySize - OSRoundUp(sizeof(JKRExpHeap), 0x10);
        heap = new (memory) JKRExpHeap(start, alignedSize, NULL, errorFlag);
        sRootHeap = heap;
    }
    heap->field_0x6e = true;
    return heap;
}

/* 802B15D0-802B16A4       .text create__10JKRExpHeapFUlP7JKRHeapb */
JKRExpHeap* JKRExpHeap::create(u32 size, JKRHeap* parent, bool errorFlag) {
    if (!parent) {
        parent = sRootHeap;
    }

    if (size == 0xffffffff) {
        size = parent->getMaxAllocatableSize(0x10);
    }

    u32 alignedSize = OSRoundDown(size, 0x10);
    u32 expHeapSize = OSRoundUp(sizeof(JKRExpHeap), 0x10);
    if (alignedSize < 0xa0)
        return NULL;

    u8* memory = (u8*)JKRAllocFromHeap(parent, alignedSize, 0x10);
    u8* dataPtr = (memory + expHeapSize);
    if (!memory) {
        return NULL;
    }

    JKRExpHeap* newHeap =
        new (memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);

    if (newHeap == NULL) {
        i_JKRFree(memory);
        return NULL;
    }

    newHeap->field_0x6e = false;
    return newHeap;
}

/* 802B16A4-802B1728       .text do_destroy__10JKRExpHeapFv */
void JKRExpHeap::do_destroy() {
    if (!field_0x6e) {
        JKRHeap* heap = mChildTree.getParent()->getObject();
        if (heap) {
            this->~JKRExpHeap();
            JKRHeap::free(this, heap);
        }
    } else {
        this->~JKRExpHeap();
    }
}

/* 802B1728-802B17B8       .text __ct__10JKRExpHeapFPvUlP7JKRHeapb */
JKRExpHeap::JKRExpHeap(void* data, u32 size, JKRHeap* parent, bool errorFlag) : JKRHeap(data, size, parent, errorFlag) {
    mAllocMode = 0;
    mCurrentGroupId = 0xff;
    mHeadFreeList = (CMemBlock*)data;
    mTailFreeList = mHeadFreeList;
    mHeadFreeList->initiate(NULL, NULL, size - sizeof(CMemBlock), 0, 0);
    mHeadUsedList = NULL;
    mTailUsedList = NULL;
}

/* 802B17B8-802B1820       .text __dt__10JKRExpHeapFv */
JKRExpHeap::~JKRExpHeap() {
    dispose();
}

/* 802B1820-802B192C       .text do_alloc__10JKRExpHeapFUli */
void* JKRExpHeap::do_alloc(u32 size, int alignment) {
    void* ptr;

    lock();
    if (size < 4) {
        size = 4;
    }

    if (alignment >= 0) {
        if (alignment <= 4) {
            ptr = allocFromHead(size);
        } else {
            ptr = allocFromHead(size, alignment);
        }
    } else {
        if (-alignment <= 4) {
            ptr = allocFromTail(size);
        } else {
            ptr = allocFromTail(size, -alignment);
        }
    }

    if (ptr == NULL) {
        JUTWarningConsole_f(":::cannot alloc memory (0x%x byte).\n", size);
        if (mErrorFlag == true) {
            callErrorHandler(this, size, alignment);
        }
    }
    unlock();

    return ptr;
}

static u32 DBfoundSize;
static u32 DBfoundOffset;
static JKRExpHeap::CMemBlock* DBfoundBlock;
static JKRExpHeap::CMemBlock* DBnewFreeBlock;
static JKRExpHeap::CMemBlock* DBnewUsedBlock;

/* 802B192C-802B1B88       .text allocFromHead__10JKRExpHeapFUli */
// wrong register at end
void* JKRExpHeap::allocFromHead(u32 size, int align) {
    /* Nonmatching */
    u32 foundOffset;
    int foundSize;
    CMemBlock* newFreeBlock;
    CMemBlock* newUsedBlock;
    CMemBlock* foundBlock;

    size = ALIGN_NEXT(size, 4);
    foundSize = -1;
    foundOffset = 0;
    foundBlock = NULL;

    for (CMemBlock* block = mHeadFreeList; block; block = block->mNext) {
        u32 offset =
            ALIGN_PREV(align - 1 + (u32)block->getContent(), align) - (u32)block->getContent();
        if (block->size < size + offset) {
            continue;
        }

        if (foundSize <= (u32)block->size) {
            continue;
        }

        foundSize = block->size;
        foundBlock = block;
        foundOffset = offset;
        if (mAllocMode != 0) {
            break;
        }

        u32 blockSize = block->size;
        if (blockSize == size) {
            break;
        }
    }

    DBfoundSize = foundSize;
    DBfoundOffset = foundOffset;
    DBfoundBlock = foundBlock;

    if (foundBlock) {
        if (foundOffset >= sizeof(CMemBlock)) {
            CMemBlock* prev = foundBlock->mPrev;
            CMemBlock* next = foundBlock->mNext;
            newUsedBlock = foundBlock->allocFore(foundOffset - sizeof(CMemBlock), 0, 0, 0, 0);

            if (newUsedBlock) {
                newFreeBlock = newUsedBlock->allocFore(size, mCurrentGroupId, 0, 0, 0);
            } else {
                newFreeBlock = NULL;
            }

            if (newFreeBlock) {
                setFreeBlock(foundBlock, prev, newFreeBlock);
            } else {
                setFreeBlock(foundBlock, prev, next);
            }

            if (newFreeBlock) {
                setFreeBlock(newFreeBlock, foundBlock, next);
            }

            appendUsedList(newUsedBlock);
            DBnewFreeBlock = newFreeBlock;
            DBnewUsedBlock = newUsedBlock;
            return newUsedBlock->getContent();
        } else {
            if (foundOffset != 0) {
                CMemBlock* prev = foundBlock->mPrev;
                CMemBlock* next = foundBlock->mNext;
                removeFreeBlock(foundBlock);
                newUsedBlock = (CMemBlock*)((u32)foundBlock + foundOffset);
                newUsedBlock->size = foundBlock->size - foundOffset;
                newFreeBlock =
                    newUsedBlock->allocFore(size, mCurrentGroupId, (u8)foundOffset, 0, 0);
                if (newFreeBlock) {
                    setFreeBlock(newFreeBlock, prev, next);
                }
                appendUsedList(newUsedBlock);
                return newUsedBlock->getContent();
            } else {
                CMemBlock* prev = foundBlock->mPrev;
                CMemBlock* next = foundBlock->mNext;
                // Works but very fake match
                /*size = (u32)foundBlock->allocFore(size, mCurrentGroupId, 0, 0, 0);
                removeFreeBlock(foundBlock);
                if (size) {
                    setFreeBlock((CMemBlock*)size, prev, next);
                }*/
                newFreeBlock = foundBlock->allocFore(size, mCurrentGroupId, 0, 0, 0);
                removeFreeBlock(foundBlock);
                if (newFreeBlock) {
                    setFreeBlock(newFreeBlock, prev, next);
                }
                appendUsedList(foundBlock);
                return foundBlock->getContent();
            }
        }
    }

    return NULL;
}

/* 802B1B88-802B1C6C       .text allocFromHead__10JKRExpHeapFUl */
void* JKRExpHeap::allocFromHead(u32 size) {
    size = ALIGN_NEXT(size, 4);
    s32 foundSize = -1;
    CMemBlock* foundBlock = NULL;
    for (CMemBlock* block = mHeadFreeList; block; block = block->getNextBlock()) {
        if (block->getSize() < size) {
            continue;
        }

        if (foundSize <= block->getSize()) {
            continue;
        }

        foundSize = block->getSize();
        foundBlock = block;
        if (mAllocMode != 0) {
            break;
        }

        if (foundSize == size) {
            break;
        }
    }

    if (foundBlock) {
        CMemBlock* newblock = foundBlock->allocFore(size, mCurrentGroupId, 0, 0, 0);
        if (newblock) {
            setFreeBlock(newblock, foundBlock->getPrevBlock(), foundBlock->getNextBlock());
        } else {
            removeFreeBlock(foundBlock);
        }
        appendUsedList(foundBlock);
        return foundBlock->getContent();
    }
    return NULL;
}

/* 802B1C6C-802B1DCC       .text allocFromTail__10JKRExpHeapFUli */
void* JKRExpHeap::allocFromTail(u32 size, int align) {
    u32 offset = 0;
    CMemBlock* foundBlock = NULL;
    CMemBlock* newBlock = NULL;
    u32 usedSize;
    u32 start;

    for (CMemBlock* block = mTailFreeList; block; block = block->mPrev) {
        start = ALIGN_PREV((u32)block->getContent() + block->size - size, align);
        usedSize = (u32)block->getContent() + block->size - start;
        if (block->size >= usedSize) {
            foundBlock = block;
            offset = block->size - usedSize;
            newBlock = (CMemBlock*)start - 1;
            break;
        }
    }

    if (foundBlock != NULL) {
        if (offset >= sizeof(CMemBlock)) {
            newBlock->initiate(NULL, NULL, usedSize, mCurrentGroupId, -0x80);
            foundBlock->size = foundBlock->size - usedSize - sizeof(CMemBlock);
            appendUsedList(newBlock);
            return newBlock->getContent();
        } else {
            if (offset != 0) {
                removeFreeBlock(foundBlock);
                newBlock->initiate(NULL, NULL, usedSize, mCurrentGroupId, offset | 0x80);
                appendUsedList(newBlock);
                return newBlock->getContent();
            } else {
                removeFreeBlock(foundBlock);
                newBlock->initiate(NULL, NULL, usedSize, mCurrentGroupId, -0x80);
                appendUsedList(newBlock);
                return newBlock->getContent();
            }
        }
    }

    return NULL;
}

/* 802B1DCC-802B1EA4       .text allocFromTail__10JKRExpHeapFUl */
void* JKRExpHeap::allocFromTail(u32 size) {
    u32 size2 = ALIGN_NEXT(size, 4);
    CMemBlock* foundBlock = NULL;
    for (CMemBlock* block = mTailFreeList; block; block = block->getPrevBlock()) {
        if (block->getSize() >= size2) {
            foundBlock = block;
            break;
        }
    }

    if (foundBlock != NULL) {
        CMemBlock* usedBlock = foundBlock->allocBack(size2, 0, 0, mCurrentGroupId, 0);
        CMemBlock* freeBlock;
        if (usedBlock) {
            freeBlock = foundBlock;
        } else {
            removeFreeBlock(foundBlock);
            usedBlock = foundBlock;
            freeBlock = NULL;
        }

        if (freeBlock) {
            setFreeBlock(freeBlock, foundBlock->getPrevBlock(), foundBlock->getNextBlock());
        }
        appendUsedList(usedBlock);
        return usedBlock->getContent();
    }
    return NULL;
}

/* 802B1EA4-802B1F44       .text do_free__10JKRExpHeapFPv */
void JKRExpHeap::do_free(void* ptr) {
    lock();
    if (getStartAddr() <= ptr && ptr <= getEndAddr()) {
        CMemBlock* block = CMemBlock::getHeapBlock(ptr);
        if (block) {
            block->free(this);
        }
    } else {
        JUT_WARN(888, "free: memblock %x not in heap %x", ptr, this);
    }
    unlock();
}

/* 802B1F44-802B1FC0       .text do_freeAll__10JKRExpHeapFv */
void JKRExpHeap::do_freeAll() {
    lock();
    JKRHeap::callAllDisposer();
    mHeadFreeList = (CMemBlock*)getStartAddr();
    mTailFreeList = mHeadFreeList;
    mHeadFreeList->initiate(NULL, NULL, getSize() - 0x10, 0, 0);
    mHeadUsedList = NULL;
    mTailUsedList = NULL;
    unlock();
}

/* 802B1FC0-802B2048       .text do_freeTail__10JKRExpHeapFv */
void JKRExpHeap::do_freeTail() {
    lock();
    for (CMemBlock* block = mHeadUsedList; block != NULL;) {
        if ((block->mFlags & 0x80) != 0) {
            dispose(block + 1, block->size);
            CMemBlock* temp = block->mNext;
            block->free(this);
            block = temp;
        } else {
            block = block->mNext;
        }
    }
    unlock();
}

/* 802B2048-802B2098       .text do_changeGroupID__10JKRExpHeapFUc */
s32 JKRExpHeap::do_changeGroupID(u8 groupId) {
    lock();
    u8 prev = mCurrentGroupId;
    mCurrentGroupId = groupId;
    unlock();
    return prev;
}

/* 802B2098-802B2244       .text do_resize__10JKRExpHeapFPvUl */
s32 JKRExpHeap::do_resize(void* ptr, u32 size) {
    lock();
    CMemBlock* block = CMemBlock::getHeapBlock(ptr);
    if (block == NULL || ptr < mStart || mEnd < ptr) {
        unlock();
        return -1;
    }

    size = ALIGN_NEXT(size, 4);
    if (size == block->size) {
        unlock();
        return size;
    }

    if (size > block->size) {
        CMemBlock* foundBlock = NULL;
        for (CMemBlock* freeBlock = mHeadFreeList; freeBlock; freeBlock = freeBlock->mNext) {
            if (freeBlock == (CMemBlock*)((u32)(block + 1) + block->size)) {
                foundBlock = freeBlock;
                break;
            }
        }

        if (foundBlock == NULL) {
            unlock();
            return -1;
        }

        if (size > block->size + sizeof(CMemBlock) + foundBlock->size) {
            unlock();
            return -1;
        }

        removeFreeBlock(foundBlock);
        block->size += foundBlock->size + sizeof(CMemBlock);
        if (block->size - size > sizeof(CMemBlock)) {
            CMemBlock* newBlock = block->allocFore(size, block->mGroupId, block->mFlags, 0, 0);
            if (newBlock) {
                recycleFreeBlock(newBlock);
            }
        }
    } else {
        if (block->size - size > sizeof(CMemBlock)) {
            CMemBlock* freeBlock = block->allocFore(size, block->mGroupId, block->mFlags, 0, 0);
            if (freeBlock) {
                recycleFreeBlock(freeBlock);
            }
        }
    }

    unlock();
    return block->size;
}

/* 802B2244-802B22C4       .text do_getSize__10JKRExpHeapFPv */
s32 JKRExpHeap::do_getSize(void* ptr) {
    lock();
    CMemBlock* block = CMemBlock::getHeapBlock(ptr);
    if (!block || ptr < getStartAddr() || getEndAddr() < ptr) {
        unlock();
        return -1;
    }
    unlock();
    return block->getSize();
}

/* 802B22C4-802B2330       .text do_getFreeSize__10JKRExpHeapFv */
s32 JKRExpHeap::do_getFreeSize() {
    lock();
    s32 size = 0;
    for (CMemBlock* block = mHeadFreeList; block; block = block->getNextBlock()) {
        if (size < (s32)block->getSize()) {
            size = block->getSize();
        }
    }
    unlock();
    return size;
}

/* 802B2330-802B23A4       .text do_getMaxFreeBlock__10JKRExpHeapFv */
void* JKRExpHeap::do_getMaxFreeBlock() {
    lock();
    s32 size = 0;
    CMemBlock* res = NULL;
    for (CMemBlock* block = mHeadFreeList; block; block = block->getNextBlock()) {
        if (size < (s32)block->getSize()) {
            size = block->getSize();
            res = block;
        }
    }
    unlock();
    return res;
}

/* 802B23A4-802B2408       .text do_getTotalFreeSize__10JKRExpHeapFv */
s32 JKRExpHeap::do_getTotalFreeSize() {
    u32 size = 0;
    lock();
    for (CMemBlock* block = mHeadFreeList; block; block = block->getNextBlock()) {
        size += block->getSize();
    }
    unlock();
    return size;
}

/* 802B2408-802B2484       .text getUsedSize__10JKRExpHeapCFUc */
s32 JKRExpHeap::getUsedSize(u8 groupId) const {
    JKRExpHeap* this2 = const_cast<JKRExpHeap*>(this);
    this2->lock();
    u32 size = 0;

    for (CMemBlock* block = mHeadUsedList; block; block = block->getNextBlock()) {
        u8 blockGroupId = block->getGroupId();
        if (blockGroupId == groupId) {
            size += block->getSize() + sizeof(CMemBlock);
        }
    }

    this2->unlock();
    return size;
}

/* 802B2484-802B24EC       .text getTotalUsedSize__10JKRExpHeapCFv */
s32 JKRExpHeap::getTotalUsedSize() const {
    JKRExpHeap* this2 = const_cast<JKRExpHeap*>(this);
    this2->lock();
    u32 size = 0;

    for (CMemBlock* block = mHeadUsedList; block; block = block->getNextBlock()) {
        size += block->getSize() + sizeof(CMemBlock);
    }

    this2->unlock();
    return size;
}

static void dummy1() {
    OSReport("newSize > 0");
    OSReport("Halt");
}

/* 802B24EC-802B2584       .text appendUsedList__10JKRExpHeapFPQ210JKRExpHeap9CMemBlock */
void JKRExpHeap::appendUsedList(CMemBlock* newblock) {
    if (!newblock) {
        OSPanic(__FILE__, 1466, ":::ERROR! appendUsedList\n");
    }

    CMemBlock* block = mTailUsedList;
    newblock->mMagic = 'HM';

    if (block) {
        block->mNext = newblock;
        newblock->mPrev = block;
    } else {
        newblock->mPrev = NULL;
    }

    mTailUsedList = newblock;
    if (!mHeadUsedList) {
        mHeadUsedList = newblock;
    }
    newblock->mNext = NULL;
}

/* 802B2584-802B25D0       .text setFreeBlock__10JKRExpHeapFPQ210JKRExpHeap9CMemBlockPQ210JKRExpHeap9CMemBlockPQ210JKRExpHeap9CMemBlock */
void JKRExpHeap::setFreeBlock(CMemBlock* block, CMemBlock* prev, CMemBlock* next) {
    if (prev == NULL) {
        mHeadFreeList = block;
        block->mPrev = NULL;
    } else {
        prev->mNext = block;
        block->mPrev = prev;
    }

    if (next == NULL) {
        mTailFreeList = block;
        block->mNext = NULL;
    } else {
        next->mPrev = block;
        block->mNext = next;
    }

    block->mMagic = 0;
}

/* 802B25D0-802B2604       .text removeFreeBlock__10JKRExpHeapFPQ210JKRExpHeap9CMemBlock */
void JKRExpHeap::removeFreeBlock(CMemBlock* block) {
    CMemBlock* prev = block->mPrev;
    CMemBlock* next = block->mNext;

    if (prev == NULL) {
        mHeadFreeList = next;
    } else {
        prev->mNext = next;
    }

    if (next == NULL) {
        mTailFreeList = prev;
    } else {
        next->mPrev = prev;
    }
}

/* 802B2604-802B2638       .text removeUsedBlock__10JKRExpHeapFPQ210JKRExpHeap9CMemBlock */
void JKRExpHeap::removeUsedBlock(CMemBlock* block) {
    CMemBlock* prev = block->mPrev;
    CMemBlock* next = block->mNext;

    if (prev == NULL) {
        mHeadUsedList = next;
    } else {
        prev->mNext = next;
    }

    if (next == NULL) {
        mTailUsedList = prev;
    } else {
        next->mPrev = prev;
    }
}

/* 802B2638-802B27D0       .text recycleFreeBlock__10JKRExpHeapFPQ210JKRExpHeap9CMemBlock */
void JKRExpHeap::recycleFreeBlock(CMemBlock* block) {
    JKRExpHeap::CMemBlock* newBlock = block;
    int size = block->size;
    void* blockEnd = (u8*)block + size;
    block->mMagic = 0;

    if ((block->mFlags & 0x7f) != 0) {
        newBlock = (CMemBlock*)((u8*)block - (block->mFlags & 0x7f));
        size += (block->mFlags & 0x7f);
        blockEnd = (u8*)newBlock + size;
        newBlock->mGroupId = 0;
        newBlock->mFlags = 0;
        newBlock->size = size;
    }

    if (!mHeadFreeList) {
        newBlock->initiate(NULL, NULL, size, 0, 0);
        mHeadFreeList = newBlock;
        mTailFreeList = newBlock;
        setFreeBlock(newBlock, NULL, NULL);
        return;
    }

    if (mHeadFreeList >= blockEnd) {
        newBlock->initiate(NULL, NULL, size, 0, 0);
        setFreeBlock(newBlock, NULL, mHeadFreeList);
        joinTwoBlocks(newBlock);
        return;
    }

    if (mTailFreeList <= newBlock) {
        newBlock->initiate(NULL, NULL, size, 0, 0);
        setFreeBlock(newBlock, mTailFreeList, NULL);
        joinTwoBlocks(newBlock->mPrev);
        return;
    }

    for (CMemBlock* freeBlock = mHeadFreeList; freeBlock; freeBlock = freeBlock->mNext) {
        if (freeBlock >= newBlock || newBlock >= freeBlock->mNext) {
            continue;
        }
        newBlock->mNext = freeBlock->mNext;
        newBlock->mPrev = freeBlock;
        freeBlock->mNext = newBlock;
        newBlock->mNext->mPrev = newBlock;
        newBlock->mGroupId = 0;
        joinTwoBlocks(newBlock);
        joinTwoBlocks(freeBlock);
        return;
    }
}

/* 802B27D0-802B291C       .text joinTwoBlocks__10JKRExpHeapFPQ210JKRExpHeap9CMemBlock */
// regalloc
void JKRExpHeap::joinTwoBlocks(CMemBlock* block) {
    /* Nonmatching */
    u32 endAddr = (u32)(block + 1) + block->size;
    CMemBlock* next = block->mNext;
    u32 nextAddr = (u32)next - (next->mFlags & 0x7f);
    if (endAddr > nextAddr) {
        JUTWarningConsole_f(":::Heap may be broken. (block = %x)", block);
        OSReport(":::block = %x\n", block);
        OSReport(":::joinTwoBlocks [%x %x %x][%x %x %x]\n", block, block->mFlags, block->size, block->mNext, block->mNext->mFlags, block->mNext->size);
        OSReport(":::: endAddr = %x\n", endAddr);
        OSReport(":::: nextAddr = %x\n", nextAddr);
        JKRGetCurrentHeap()->dump();
        OSPanic(__FILE__, 1718, ":::: Bad Block\n");
    }

    if (endAddr == nextAddr) {
        block->size = next->size + sizeof(CMemBlock) + next->getAlignment() + block->size;
        setFreeBlock(block, block->mPrev, next->mNext);
    }
}

/* 802B291C-802B2B44       .text check__10JKRExpHeapFv */
bool JKRExpHeap::check() {
    lock();
    int totalBytes = 0;
    bool ok = true;
    for (CMemBlock* block = mHeadUsedList; block; block = block->mNext) {
        if (!block->isValid()) {
            ok = false;
            JUTWarningConsole_f(":::addr %08x: bad heap signature. (%c%c)\n", block,
                                JSUHiByte(block->mMagic), JSULoByte(block->mMagic));
        }

        if (block->mNext) {
            if (!block->mNext->isValid()) {
                ok = false;
                JUTWarningConsole_f(":::addr %08x: bad next pointer (%08x)\nabort\n", block,
                                    block->mNext);
                break;
            }
            if (block->mNext->mPrev != block) {
                ok = false;
                JUTWarningConsole_f(":::addr %08x: bad previous pointer (%08x)\n", block->mNext,
                                    block->mNext->mPrev);
            }
        } else {
            if (mTailUsedList != block) {
                ok = false;
                JUTWarningConsole_f(":::addr %08x: bad used list(REV) (%08x)\n", block,
                                    mTailUsedList);
            }
        }
        totalBytes += sizeof(CMemBlock) + block->size + block->getAlignment();
    }

    for (CMemBlock* block = mHeadFreeList; block; block = block->mNext) {
        totalBytes += block->size + sizeof(CMemBlock);
        if (block->mNext) {
            if (block->mNext->mPrev != block) {
                ok = false;
                JUTWarningConsole_f(":::addr %08x: bad previous pointer (%08x)\n", block->mNext,
                                    block->mNext->mPrev);
            }

            if ((u32)block + block->size + sizeof(CMemBlock) > (u32)block->mNext) {
                ok = false;
                JUTWarningConsole_f(":::addr %08x: bad block size (%08x)\n", block, block->size);
            }
        } else {
            if (mTailFreeList != block) {
                ok = false;
                JUTWarningConsole_f(":::addr %08x: bad used list(REV) (%08x)\n", block,
                                    mTailFreeList);
            }
        }
    }

    if (totalBytes != mSize) {
        ok = false;
        JUTWarningConsole_f(":::bad total memory block size (%08X, %08X)\n", mSize, totalBytes);
    }

    if (!ok) {
        JUTWarningConsole(":::there is some error in this heap!\n");
    }

    unlock();
    return ok;
}

/* 802B2B44-802B2B48       .text do_freeFill__10JKRExpHeapFv */
void JKRExpHeap::do_freeFill() {}

/* 802B2B48-802B2D5C       .text dump__10JKRExpHeapFv */
bool JKRExpHeap::dump() {
    lock();
    bool result = check();
    u32 usedBytes = 0;
    u32 usedCount = 0;
    u32 freeCount = 0;

    JUTReportConsole(" attr  address:   size    gid aln   prev_ptr next_ptr\n");
    JUTReportConsole("(Used Blocks)\n");
    if (!mHeadUsedList) {
        JUTReportConsole(" NONE\n");
    }

    for (CMemBlock* block = mHeadUsedList; block; block = block->mNext) {
        if (!block->isValid()) {
            JUTReportConsole_f("xxxxx %08x: --------  --- ---  (-------- --------)\nabort\n",
                               block);
            break;
        }

        JUTReportConsole_f("%s %08x: %08x  %3d %3d  (%08x %08x)\n",
                           block->_isTempMemBlock() ? " temp" : "alloc", block->getContent(),
                           block->size, block->mGroupId, block->getAlignment(), block->mPrev,
                           block->mNext);
        usedBytes += sizeof(CMemBlock) + block->size + block->getAlignment();
        usedCount++;
    }

    JUTReportConsole("(Free Blocks)\n");
    if (!mHeadFreeList) {
        JUTReportConsole(" NONE\n");
    }

    for (CMemBlock* block = mHeadFreeList; block; block = block->mNext) {
        JUTReportConsole_f("%s %08x: %08x  %3d %3d  (%08x %08x)\n", " free", block->getContent(),
                           block->size, block->mGroupId, block->getAlignment(), block->mPrev,
                           block->mNext);
        freeCount++;
    }

    float percent = ((float)usedBytes / (float)mSize) * 100.0f;
    JUTReportConsole_f("%d / %d bytes (%6.2f%%) used (U:%d F:%d)\n", usedBytes, mSize, percent,
                       usedCount, freeCount);
    unlock();
    return result;
}

/* 802B2D5C-802B2F5C       .text dump_sort_by_address__10JKRExpHeapFv */
// regalloc, stack
bool JKRExpHeap::dump_sort_by_address() {
    /* Nonmatching */
    lock();
    bool result = check();
    u32 usedBytes = 0;
    u32 usedCount = 0;
    u32 freeCount = 0;
    JUTReportConsole(" attr  address:   size    gid aln   prev_ptr next_ptr\n");

    CMemBlock* block;
    for (CMemBlock* var1 = NULL; true; var1 = block) {
        CMemBlock* block = (CMemBlock*)0xffffffff;
        for (CMemBlock* iterBlock = mHeadFreeList; iterBlock; iterBlock = iterBlock->getNextBlock()) {
            if (var1 < iterBlock && iterBlock < block) {
                block = iterBlock;
            }
        }

        for (CMemBlock* iterBlock = mHeadUsedList; iterBlock; iterBlock = iterBlock->getNextBlock()) {
            if (var1 < iterBlock && iterBlock < block) {
                block = iterBlock;
            }
        }

        if (block == (CMemBlock*)0xffffffff) {
            break;
        }

        if (block->mMagic == 0) {
            JUTReportConsole_f("%s %08x: %08x  %3d %3d  (%08x %08x)\n", " free", block->getContent(), block->getSize(), block->getGroupId(), block->getAlignment(), block->getPrevBlock(), block->getNextBlock());
            freeCount += 1;
            continue;
        }

        if (!block->isValid()) {
            JUTReportConsole_f("xxxxx %08x: --------  --- ---  (-------- --------)\nabort\n");
            break;
        }

        int offset = block->getAlignment();
        void* content = block->getContent();
        const char* type = block->_isTempMemBlock() ? " temp" : "alloc";
        JUTReportConsole_f("%s %08x: %08x  %3d %3d  (%08x %08x)\n", type, content, block->getSize(),
                            block->getGroupId(), offset, block->getPrevBlock(), block->getNextBlock());
        usedBytes += sizeof(CMemBlock) + block->size + block->getAlignment();
        usedCount++;
    }

    float percent = ((float)usedBytes / (float)mSize) * 100.0f;
    JUTReportConsole_f("%d / %d bytes (%6.2f%%) used (U:%d F:%d)\n", usedBytes, mSize, percent,
                       usedCount, freeCount);
    unlock();
    return result;
}

/* 802B2F5C-802B2F7C       .text dump_sort__10JKRExpHeapFv */
bool JKRExpHeap::dump_sort() {
    return dump_sort_by_address();
}

/* 802B2F7C-802B2F9C       .text initiate__Q210JKRExpHeap9CMemBlockFPQ210JKRExpHeap9CMemBlockPQ210JKRExpHeap9CMemBlockUlUcUc */
void JKRExpHeap::CMemBlock::initiate(CMemBlock* prev, CMemBlock* next, u32 size, u8 groupId, u8 alignment) {
    mMagic = 'HM';
    mFlags = alignment;
    mGroupId = groupId;
    this->size = size;
    mPrev = prev;
    mNext = next;
}

/* 802B2F9C-802B2FE0       .text allocFore__Q210JKRExpHeap9CMemBlockFUlUcUcUcUc */
JKRExpHeap::CMemBlock* JKRExpHeap::CMemBlock::allocFore(u32 size, u8 groupId1, u8 alignment1, u8 groupId2, u8 alignment2) {
    CMemBlock* block = NULL;
    mGroupId = groupId1;
    mFlags = alignment1;
    if (getSize() >= size + sizeof(CMemBlock)) {
        block = (CMemBlock*)(size + (u32)this);
        block[1].mGroupId = groupId2;
        block[1].mFlags = alignment2;
        block[1].size = this->size - (size + sizeof(CMemBlock));
        this->size = size;
        block = block + 1;
    }
    return block;
}

/* 802B2FE0-802B3038       .text allocBack__Q210JKRExpHeap9CMemBlockFUlUcUcUcUc */
JKRExpHeap::CMemBlock* JKRExpHeap::CMemBlock::allocBack(u32 size, u8 groupId1, u8 alignment1, u8 groupId2, u8 alignment2) {
    CMemBlock* newblock = NULL;
    if (getSize() >= size + sizeof(CMemBlock)) {
        newblock = (CMemBlock*)((u32)this + getSize() - size);
        newblock->mGroupId = groupId2;
        newblock->mFlags = alignment2 | 0x80;
        newblock->size = size;
        mGroupId = groupId1;
        mFlags = alignment1;
        this->size -= size + sizeof(CMemBlock);
    } else {
        mGroupId = groupId2;
        mFlags = 0x80;
    }
    return newblock;
}

/* 802B3038-802B3088       .text free__Q210JKRExpHeap9CMemBlockFP10JKRExpHeap */
int JKRExpHeap::CMemBlock::free(JKRExpHeap* heap) {
    heap->removeUsedBlock(this);
    heap->recycleFreeBlock(this);
    return 0;
}

/* 802B3088-802B30A4       .text getHeapBlock__Q210JKRExpHeap9CMemBlockFPv */
JKRExpHeap::CMemBlock* JKRExpHeap::CMemBlock::getHeapBlock(void* ptr) {
    if (ptr) {
        CMemBlock* block = (CMemBlock*)ptr - 1;
        if (block->isValid()) {
            return block;
        }
    }
    return NULL;
}

static void dummy2() {
    OSReport("+---------------JKRExpHeap\n");
    OSReport("|         Align Group  size    ( prev , next )\n");
    OSReport("| ---- FreeFirst\n");
    OSReport("| %08x  ");
    OSReport("%2x  %3d  %6x  (%08x %08x)\n");
    OSReport("| ---- FreeLast\n");
    OSReport("| ---- UsedFirst\n");
    OSReport("| ---- UsedLast\n");
    OSReport("+---------------End\n");
}

/* 802B30A4-802B31D4       .text state_register__10JKRExpHeapCFPQ27JKRHeap6TStateUl */
void JKRExpHeap::state_register(TState* p, u32 param_1) const {
    JUT_ASSERT(2423, p != 0);
    JUT_ASSERT(2424, p->getHeap() == this);
    p->mId = param_1;
    if (param_1 <= 0xff) {
        p->mUsedSize = getUsedSize(param_1);
    } else {
        s32 freeSize = const_cast<JKRExpHeap*>(this)->getTotalFreeSize();
        p->mUsedSize = getSize() - freeSize;
    }

    u32 checkCode = 0;
    for (CMemBlock* block = mHeadUsedList; block; block = block->getNextBlock()) {
        if (param_1 <= 0xff) {
            u8 groupId = block->getGroupId();
            if (groupId == param_1) {
                checkCode += (u32)block * 3;
            }
        } else {
            checkCode += (u32)block * 3;
        }
    }
    p->mCheckCode = checkCode;
}

/* 802B31D4-802B327C       .text state_compare__10JKRExpHeapCFRCQ27JKRHeap6TStateRCQ27JKRHeap6TState */
bool JKRExpHeap::state_compare(const JKRHeap::TState& r1, const JKRHeap::TState& r2) const {
    JUT_ASSERT(2471, r1.getHeap() == r2.getHeap());
    bool result = true;
    if (r1.mCheckCode != r2.mCheckCode) {
        result = false;
    }

    if (r1.mUsedSize != r2.mUsedSize) {
        result = false;
    }

    return result;
}

/* 802B327C-802B3288       .text getHeapType__10JKRExpHeapFv */
u32 JKRExpHeap::getHeapType() {
    return 'EXPH';
}

/* 802B3288-802B3290       .text do_getCurrentGroupId__10JKRExpHeapFv */
u8 JKRExpHeap::do_getCurrentGroupId() {
    return mCurrentGroupId;
}
