#include <ultra64.h>
#include "core1/core1.h"
#include "functions.h"
#include "variables.h"
#include "version.h"
/* 
 *  Every chunk of allocated memory is prefixed with a HeapHeader.
 * 
 *  Chunks are 0x10 aligned, and cannot have a capacity < 1.
 *  This means the smallest chunksize with the header is 0x20.
 * 
 *  If a chunk is empty, it's contains ptrs to the previous and 
 *  next empty chunks. This forms a link list over all the empty
 *  chunks (EmptyHeapBlock)
 */
extern void func_80253010(void *dest, void *src, s32 size);

#define chunkSize(s) ((u32)(s)->next - (u32)(s) - sizeof(HeapHeader))
#if VERSION == VERSION_USA_1_0
#define HEAP_SIZE 0x210520
#elif VERSION == VERSION_PAL
#define HEAP_SIZE 0x211120
#endif
#define LAST_HEAP_BLOCK HEAP_SIZE/sizeof(EmptyHeapBlock) - 1
typedef enum {
    HEAP_BLOCK_EMPTY = 0,
    HEAP_BLOCK_USED  = 1,
    HEAP_BLOCK_PERM  = 2
} heap_block_type_e;

typedef struct heap_header{
    struct heap_header * prev;
    struct heap_header * next;
    u8 pad8[4];
    u32 unusedBytes_C_31:24; //size?
    u32 unkC_7:2; //state?
    u32 padC_5:6;
}HeapHeader;

typedef struct empty_heap_block{
    HeapHeader hdr;
    struct empty_heap_block *prev_free;
    struct empty_heap_block *next_free;
    u8 pad18[0x8];
} EmptyHeapBlock;

extern EmptyHeapBlock D_8002D500[LAST_HEAP_BLOCK + 1];
extern EmptyHeapBlock D_8023DA00;

/* .data */
u32 heap_occupiedBytes = 0; //occupied heap size
u8 D_80276594 = 0;
u8 D_80276598 = 0;
void *D_8027659C = NULL;
void *D_802765A0 = NULL;
s32 D_802765A4 = 0;
void *D_802765A8 = NULL;
s32 D_802765AC = 0;
struct{
    bool unk0;
}D_802765B0 = {0};
UNK_TYPE(void *) D_802765B4 = NULL;

/* .bss */
s32 D_80283220;
void *D_80283224;
void *D_80283228;
s32 D_8028322C;
u32 heap_requested_size;
HeapHeader * D_80283234;
struct {
    void *unk0[0x10];
    void **unk40;
}D_80283238;



EmptyHeapBlock *func_802549BC(s32 size);
void _heap_sortEmptyBlock(EmptyHeapBlock * arg0);
void func_80255ACC(void);

/* .code */
s32 __heap_align(s32 size){
    s32 misalign = size & 0xf;
    return(misalign)? (size - misalign + 0x10) : size;
}

int func_80254490(int arg0){
    return FALSE;
}

void _heap_defragEmptyBlock(EmptyHeapBlock * arg0){
    EmptyHeapBlock * defrag_ptr = NULL;
    if(arg0->hdr.next->unkC_7 == HEAP_BLOCK_EMPTY){ //absorb next block
        defrag_ptr = arg0;
        //remove next from empty block link list
        ((EmptyHeapBlock *)arg0->hdr.next)->next_free->prev_free = ((EmptyHeapBlock *)arg0->hdr.next)->prev_free;
        ((EmptyHeapBlock *)arg0->hdr.next)->prev_free->next_free = ((EmptyHeapBlock *)arg0->hdr.next)->next_free;
        //remove next from block link list
        arg0->hdr.next->next->prev = arg0;
        arg0->hdr.next = arg0->hdr.next->next;
    }
    if(arg0->hdr.prev->unkC_7 == HEAP_BLOCK_EMPTY){
        defrag_ptr = arg0->hdr.prev;
        //remove self from empty block link list
        arg0->next_free->prev_free = arg0->prev_free;
        arg0->prev_free->next_free = arg0->next_free;
        //remove self from block link list
        arg0->hdr.prev->next = arg0->hdr.next;
        arg0->hdr.next->prev = arg0->hdr.prev;
    }
    if(defrag_ptr != NULL){
        _heap_sortEmptyBlock(defrag_ptr);
    }else{
        _heap_sortEmptyBlock(arg0);
    }   
}

void func_8025456C(EmptyHeapBlock * arg0){
    arg0->hdr.unkC_7 = HEAP_BLOCK_EMPTY;
    arg0->hdr.unusedBytes_C_31 = 0;
    if((u8*)arg0->hdr.next - (u8*)arg0 < 10000){
        arg0->prev_free = &D_8002D500;
        arg0->next_free = D_8002D500->next_free;
        D_8002D500->next_free->prev_free = arg0;
        D_8002D500->next_free = arg0;
    }else{
        arg0->prev_free = D_8002D500[LAST_HEAP_BLOCK].prev_free;
        arg0->next_free = & D_8002D500[LAST_HEAP_BLOCK];
        
        D_8002D500[LAST_HEAP_BLOCK].prev_free->next_free = arg0;
        D_8002D500[LAST_HEAP_BLOCK].prev_free = arg0;

    }
    _heap_defragEmptyBlock(arg0);
}

void memcpy(void * dst, void *src, int size){
    while(size > 0){
        *(u8*)dst = *(u8*)src;
        size--;
        dst = (u8 *) dst + 1;
        src = (u8 *) src + 1;
    }
}

void wmemcpy(void * dst, void *src, int size){
    while(size > 0){
        *(u32*)dst = *(u32*)src;
        size -= 4;
        dst = (u32 *) dst + 1;
        src = (u32 *) src + 1;
    }
}

void memmove(u8* dst, u8* src, s32 n) {
    if(dst < src){ //copy
        while(n--){
            *(dst++) = *(src++);
        }
    }else{ //copy backwards to avoid data lose
        dst += n -1;
        src += n -1;
        while(n--){
            *(dst--) = *(src--);
        }
    }
}

s32 heap_get_size(void){ return HEAP_SIZE; }

s32 func_802546DC(void){ return 0; }

u32 func_802546E4(void * arg0){
    HeapHeader *sPtr = (HeapHeader *)arg0 - 1;
    return chunkSize(sPtr) - sPtr->unusedBytes_C_31;
}

void func_802546FC(void){
    D_80283224 = NULL;
    D_80283228 = NULL;
}

void heap_init(void){
    bzero(D_8002D500, HEAP_SIZE);
    func_802546FC();
    D_80283238.unk40 = &D_80283238.unk0[0];
    heap_occupiedBytes = 0;
    D_80276594 = 0;
    D_802765A0 = 0;
    D_802765A4 = 0;
    D_802765A8 = 0;
    D_802765AC = 0;
    D_802765B0.unk0 = FALSE;
    D_8002D500[0].hdr.prev = NULL;
    D_8002D500[0].hdr.next = &D_8002D500[1];
    D_8002D500[0].hdr.unkC_7 = 2;
    D_8002D500[0].hdr.unusedBytes_C_31 = 0;
    D_8002D500[0].prev_free = NULL;
    D_8002D500[0].next_free = &D_8002D500[1];

    D_8002D500[1].hdr.prev = &D_8002D500[0];
    D_8002D500[1].hdr.next = &D_8002D500[LAST_HEAP_BLOCK];
    D_8002D500[1].hdr.unkC_7 = 0;
    D_8002D500[1].hdr.unusedBytes_C_31 = 0;
    D_8002D500[1].prev_free = &D_8002D500[0];
    D_8002D500[1].next_free = &D_8002D500[LAST_HEAP_BLOCK];

    D_8002D500[LAST_HEAP_BLOCK].hdr.prev = &D_8002D500[1];
    D_8002D500[LAST_HEAP_BLOCK].hdr.next = &D_8002D500[LAST_HEAP_BLOCK + 1];
    D_8002D500[LAST_HEAP_BLOCK].hdr.unkC_7 = 2;
    D_8002D500[LAST_HEAP_BLOCK].hdr.unusedBytes_C_31 = 0;
    D_8002D500[LAST_HEAP_BLOCK].prev_free = &D_8002D500[1];
    D_8002D500[LAST_HEAP_BLOCK].next_free = NULL;
    sns_init_base_payloads();
}

void *func_8025484C(s32 size){
    D_802765B4 = malloc(ALIGN((u32)&D_8002D500[1] + 0x100, 0x100)  - (u32)&D_8002D500[1] - sizeof(EmptyHeapBlock));
    return malloc(0x80);
}

void *func_80254898(s32 arg0){
    void * sp1C = malloc(ALIGN(((u32)&D_8002D500[LAST_HEAP_BLOCK] - (u32)D_8002D500[LAST_HEAP_BLOCK].prev_free) - 0x2FF, 0x100) + - sizeof(EmptyHeapBlock));
    void * sp18 = malloc(0x80);
    free(sp1C);
    free(D_802765B4);
    D_802765B4 =  NULL;
    return sp18;
}

void func_80254908(void){
    if(D_802765A0){
        free(D_802765A0);
        D_802765A0 = NULL;
    }

    if(D_802765A8){
        free(D_802765A8);
        D_802765A8 = NULL;
    }
}

static u32 _heap_get_occupied_size(void){
    return heap_occupiedBytes;
}

u32 heap_get_occupied_size(void){
    return _heap_get_occupied_size();
}

bool func_8025498C(s32 size){
    s32 v0 = func_802549BC(size);
    return BOOL(v0);
}

EmptyHeapBlock *func_802549BC(s32 size){
    EmptyHeapBlock *a1;
    s32 aligned_size;
    u32 block_size;

    a1 = D_8002D500->next_free;
    aligned_size = __heap_align(size > 0 ? size : 1);
    while( chunkSize(&a1->hdr) < aligned_size && a1->next_free != &D_8002D500[LAST_HEAP_BLOCK] ){
        a1 = a1->next_free;
    }
   return (chunkSize(&a1->hdr) < aligned_size)? 0 : a1;
}

EmptyHeapBlock *func_80254A60(bool arg0){
        EmptyHeapBlock *v1;
        EmptyHeapBlock *v0;
    if(!arg0){
        //from start
        v1 = D_8002D500->next_free;
        while( chunkSize(&v1->hdr) < heap_requested_size && v1->next_free != &D_8002D500[LAST_HEAP_BLOCK] ){
            v1 = v1->next_free;
        }
        
        if(chunkSize(&v1->hdr) < heap_requested_size)
            return NULL;
        return v1;
    }else{
        //from back
        v1 = NULL;
        v0 = D_8002D500->next_free;
        while(v0 != &D_8002D500[LAST_HEAP_BLOCK]){
            if(chunkSize(&v0->hdr) >= heap_requested_size && v1 < v0)
                v1 = v0;
            v0 = v0->next_free;
        }

        if(!v1)
            return NULL;

        if(chunkSize(&v1->hdr) < heap_requested_size)
            return NULL;
        return v1;
    }
}

EmptyHeapBlock *func_80254B84(s32 arg0){
    if(D_80283234){
        return func_80254A60(1); //closest to back
    }else{
        return func_80254A60(0); //closest to from
    }
}

int func_80254BC4(int arg0){
    return FALSE;
}

//returns n'th free block and size
void *func_80254BD0(s32 *size, u32 arg1) {
    EmptyHeapBlock *var_v1;

    var_v1 = &D_8023DA00;
    while(arg1 != 0){
        var_v1 = var_v1->prev_free;
        if (var_v1 == &D_8002D500[0]) {
            //less than n blocks
            return NULL;
        }
        arg1--;
    }
    *size = ((s32)(var_v1->hdr.next) - (s32)var_v1) - sizeof(HeapHeader);
    return (s32)var_v1 + 0x10;
}

void func_80254C98(void){
    D_802765B0.unk0 = TRUE;
}

void *malloc(s32 size){
    u32 capacity;
    EmptyHeapBlock *v1;
    EmptyHeapBlock *a0;

    D_80283234 = D_802765B0.unk0;
    D_802765B0.unk0 = FALSE;
    if(D_8002D500->next_free == &D_8002D500[LAST_HEAP_BLOCK])
        return NULL;

    heap_requested_size = __heap_align((size > 0 )? size : 1); 
    if(!(v1 = func_80254B84(0))){ //remove stall cache ptrs
        D_80283234 = NULL;
        func_803306C8(2);
        if(!func_80254B84(0))
            func_8030A850(2);

        if(!func_80254B84(0))
            animCache_flushStale();

        if(!func_80254B84(0))
            animBinCache_flushStale(0); //nonpersistent anim

        if(!func_80254B84(0))
            func_8032AD7C(2);

        if(!(v1 = func_80254B84(0))){
            func_8033B61C();
            func_802E49E0();
            func_803306C8(3); //modelCache

            if(!func_80254B84(0))
                func_8030A850(3); //propModelCache
            
            if(!func_80254B84(0))
                func_8032AD7C(2); //actorArray

            if(!(v1 = func_80254B84(0))){
                if(!func_80254B84(0))
                    func_802F1294(); //particleEmitters
                
                if(!func_80254B84(0))
                    animBinCache_flushStale(1); //persistent anim

                if(v1 = func_80254B84(0)){}
                else
                    return NULL;
            }
        }//L80254E38
    }//L80254E38

    if( heap_requested_size + sizeof(HeapHeader) < chunkSize(&v1->hdr)){
        if(D_80283234){
            //reverse split chunk => //split empty chunk: |prev| a0 |next| => |prev| a0 | v1 |next|
            a0 = v1;
            v1 = (EmptyHeapBlock *)((u32)v1->hdr.next - (heap_requested_size + sizeof(HeapHeader)));
            v1->hdr.next = a0->hdr.next;
            a0->hdr.next->prev = v1;
            a0->hdr.next = v1;
            v1->hdr.prev = a0;
            _heap_sortEmptyBlock(a0);
        }
        else{//L80254EA4
            //split chunk: |prev| v1 |next| => |prev| v1 | __a0__ |next|
            a0 = (HeapHeader *)((u32)v1 + (heap_requested_size + sizeof(HeapHeader)));
            a0->next_free = v1->next_free;
            a0->prev_free = v1->prev_free;
            a0->next_free->prev_free = a0;
            a0->prev_free->next_free = a0;
            a0->hdr.prev = v1;
            a0->hdr.next = v1->hdr.next;
            a0->hdr.next->prev = a0;
            a0->hdr.unkC_7 = 0;
            a0->hdr.unusedBytes_C_31 = 0;
            v1->hdr.next = a0;
            _heap_sortEmptyBlock(a0);
        }
    }
    else{//L80254F08
        //use full chunk/ remove chunk from empty chunk link list
        v1->next_free->prev_free = v1->prev_free;
        v1->prev_free->next_free = v1->next_free;
    }//L80254F20
    capacity = (u32)v1->hdr.next - (u32)v1 ;
    v1->hdr.unusedBytes_C_31 = capacity - size - 0x10;
    v1->hdr.unkC_7 = 1;
    heap_occupiedBytes += capacity;
    return (u8*)v1 + sizeof(HeapHeader);
}

void func_80254F90(void){
    int i;
    for(i = 0; i < 50; i++){
        func_80255ACC();
    }
}

void _heap_sortEmptyBlock(EmptyHeapBlock * arg0){
    EmptyHeapBlock *v0 = arg0;
    EmptyHeapBlock *v1;
    EmptyHeapBlock *a2 = &D_8002D500[LAST_HEAP_BLOCK];

    //move arg0 back while larger than next
    while( arg0->next_free < &D_8002D500[LAST_HEAP_BLOCK]
        && (s32)chunkSize(&v0->next_free->hdr) + 0x10 < (s32)chunkSize(&v0->hdr) + 0x10
    ){  
        v1 = arg0->next_free;
        arg0->next_free = v1->next_free;
        v1->next_free->prev_free = arg0;
        v1->next_free = arg0;
        arg0->prev_free->next_free = v1;
        v1->prev_free = arg0->prev_free;
        arg0->prev_free = v1;
    }
    
    //move arg0 foward while smaller prev
    while( ( (v1 = arg0->prev_free) > &D_8002D500[0])
        && (s32)chunkSize(&v0->hdr) + 0x10 < (s32)chunkSize(&v0->prev_free->hdr) + 0x10
    ){
        a2 = arg0->prev_free;

        arg0->next_free->prev_free = a2;
        a2->next_free = arg0->next_free;
        arg0->next_free = a2;
        arg0->prev_free = a2->prev_free;
        a2->prev_free->next_free = arg0;
        a2->prev_free = arg0;
    }
}

void free(void * ptr){
    HeapHeader *sPtr; //stack_ptr
    
    if(ptr){
        sPtr = (HeapHeader *) ptr - 1;
        heap_occupiedBytes =  heap_occupiedBytes - (u32)((u8*)sPtr->next - (u8*)ptr) - sizeof(HeapHeader);
        
        func_8025456C(sPtr);
        
        if((u32)ptr == (u32)D_802765A0)
            D_802765A0 = NULL;

        if((u32)ptr == (u32)D_802765A8)
            D_802765A8 = NULL;
    }
}

void func_80255170(void **arg0){
    *D_80283238.unk40 = *arg0;
    D_80283238.unk40++;
    *arg0 = NULL;
}

//heap_free_queue_flush
void func_80255198(void){
    while(D_80283238.unk40 > &D_80283238.unk0[0]){
        D_80283238.unk40--;
        free(*D_80283238.unk40);
    }
}

//resizes and fragments a block;
void func_80255200(HeapHeader *block, s32 size){
    u32 remaining_bytes;
    EmptyHeapBlock *a0;
    u32 tmp, tmp2, tmp3;

    block->unusedBytes_C_31 = chunkSize(block) - size;
    tmp = ((u32) (block)->next) - ((u32) (block));
    if(size > 0)
    {
        // tmp2 = chunkSize(block) - sizeof(HeapHeader);
        tmp2 = __heap_align(size);
        remaining_bytes = chunkSize(block) - tmp2;
    }
    else{
        remaining_bytes =  tmp - sizeof(EmptyHeapBlock);
    }
    
    if(remaining_bytes >= sizeof(EmptyHeapBlock)){
        tmp = (chunkSize(block) + sizeof(HeapHeader)) - remaining_bytes;
        if(tmp);
        a0 = (s32)block + tmp;
        heap_occupiedBytes -= remaining_bytes;

        a0->hdr.prev = block;
        a0->hdr.next = block->next;
        block->next = &a0->hdr;
        a0->hdr.next->prev = &a0->hdr;
        block->unusedBytes_C_31 = chunkSize(block) - size;
        func_8025456C(a0);
    }
}

void func_80255300(HeapHeader *block, s32 size){
    func_80255200(block, size);
    if(block->next->unkC_7 == HEAP_BLOCK_EMPTY){
        _heap_sortEmptyBlock(block->next);
    }
}

void *func_80255340(void){
    return D_80283224;
}

void *func_8025534C(void){
    return D_80283228;
}

void *realloc(void *ptr, s32 size){
    
    HeapHeader *sPtr;
    void *newSeg;
    EmptyHeapBlock *emptySeg;
    
    
    D_80283224 = ptr;
    D_80283228 = ptr;
    sPtr = (HeapHeader *)ptr - 1;
    if(!((u32)((u8*) sPtr->next - (u8*)ptr) < size)){ 
        //current pointer has enough free space to accomidate size change
        func_80255300(sPtr, size);
        return ptr;
    }

    D_8027659C = ptr;
    emptySeg = (EmptyHeapBlock*) sPtr->next;
    if( emptySeg->hdr.unkC_7 == HEAP_BLOCK_EMPTY
        && !((u32)((u8*)emptySeg->hdr.next - (u8*)sPtr) - 0x10 < size)
    ){//combine current heap segment with the next one (if next one is free).
        //remove empty segment from list
        emptySeg->next_free->prev_free = emptySeg->prev_free;
        emptySeg->prev_free->next_free = emptySeg->next_free;
        heap_occupiedBytes += (u8*)emptySeg->hdr.next - (u8*)emptySeg;
        sPtr->next = emptySeg->hdr.next;
        emptySeg->hdr.next->prev = sPtr;
        func_80255300(sPtr, size);
        D_8027659C = 0;
        return ptr;
    }//L80255430

    if(!(newSeg = malloc(size))){
        return 0;
    }

    func_80253010(newSeg, ptr, __heap_align(size));
    free(ptr);
    ptr = newSeg;
    D_8027659C = 0;
    D_80283228 = newSeg;

    if(newSeg);
    
    return ptr;
}

u32 heap_get_free_size(void){
    return HEAP_SIZE - heap_get_occupied_size();
}

s32 heap_findLargestEmptyBlock(s32 *size_ptr){
    EmptyHeapBlock *v0;
    s32 i;
    s32 size;

    v0 = D_8002D500->next_free;
    *size_ptr = 0;
    i = 0;
    while(v0 != &D_8002D500[LAST_HEAP_BLOCK]){
        size = (s32)v0->hdr.next - (s32)v0;
        *size_ptr = (size < *size_ptr) ? *size_ptr : size;
        v0 = v0->next_free;
        i++;
    }
    return i;
}

void func_80255524(void){
    D_80283220 = (D_80276598)? -6000000 : 0;

    if(D_802765A0 && D_802765A4 + 1 < D_802765AC){
        free(D_802765A0);
        D_802765A0 = NULL;

        if(D_802765A8){
            free(D_802765A8);
            D_802765A8 = NULL;
        }
    }
}

void func_802555C4(void){
    D_8028322C = FALSE;
}

bool func_802555D0(void){
    return D_8028322C;
}

void *defrag(void *this){
    HeapHeader *new_block;
    HeapHeader *this_block;
    EmptyHeapBlock *new_empty;
    EmptyHeapBlock *prev_empty;
    EmptyHeapBlock *next_empty;
    HeapHeader *prev_block;
    s32 size;

    if(this == NULL || this == D_8027659C){
        return this;
    }
    
    size = (s32)((HeapHeader*)this)[-1].next - (s32)this + 0x10;

    this_block = &((HeapHeader*)this)[-1];
    if(D_80283220 + size >= 1000000){
        return this;
    }
    new_block = this_block->prev;
    
    if(new_block->unkC_7 != HEAP_BLOCK_EMPTY || chunkSize(new_block) < 0x10){
        return this;
    }

    //previous block is empty, move contents of this block forward
    D_8028322C = TRUE;
    D_80283220 = D_80283220 + size;

    next_empty = ((EmptyHeapBlock *)new_block)->next_free;
    prev_empty = ((EmptyHeapBlock *)new_block)->prev_free;
    prev_block = new_block->prev;
    func_80253010(new_block, this_block, size);
    //create new empty block at end of new_block;
    new_empty = (EmptyHeapBlock *)((s32)new_block + size);
    new_empty->hdr.prev = new_block;
    new_empty->hdr.next = new_block->next;
    new_block->next = &new_empty->hdr;
    new_block->prev = prev_block;
    new_empty->hdr.next->prev = &new_empty->hdr;
    prev_empty->next_free = new_empty;
    next_empty->prev_free = new_empty;
    new_empty->next_free = next_empty;
    new_empty->prev_free = prev_empty;
    new_empty->hdr.unkC_7 = HEAP_BLOCK_EMPTY;
    new_empty->hdr.unusedBytes_C_31 = 0;

    if(new_block);

    _heap_defragEmptyBlock(new_empty); //combine new_empty with any surrounding empty blocks
    return  (void *)((s32)new_block + sizeof(HeapHeader));
}

void *defrag_asset(void *arg0){
    void *sp1C;
    if(arg0 == NULL || arg0 == D_8027659C)
        return arg0;

    sp1C = defrag(arg0);
    assetcache_update_ptr(arg0, sp1C);
    return sp1C;
}

//recache??? defrag_cache???
void *func_80255774(void *this){
    HeapHeader *this_block;
    HeapHeader *prev_block;
    s32 size;
    s32 pad;
    void *sp24;

    if( this == NULL 
        || this == D_8027659C
        || D_802765A0
        || func_8033BD8C(this)
    ){
        return this;
    }

    size = (s32)((HeapHeader*)this)[-1].next - (s32)this + 0x10;
    this_block = &((HeapHeader*)this)[-1];

    if(D_80283220 + size >= 1000000)
        return this;

    prev_block = this_block->prev;
    if(prev_block->unkC_7 != HEAP_BLOCK_EMPTY){
        return this;
    }

    sp24 = malloc(size - sizeof(HeapHeader));
    func_80253010(sp24, this, size - sizeof(HeapHeader));
    osWritebackDCache(sp24, size - sizeof(HeapHeader));
    D_80283220 += size  - sizeof(HeapHeader);
    D_802765A0 = this;
    D_802765A4 = D_802765AC;
    return sp24;
}

//recache asset?? defragment cached obj???
void *func_80255888(void *arg0){
    void *sp1C;
    if(arg0 == NULL || arg0 == D_8027659C){
        return arg0;
    }

    sp1C = func_80255774(arg0);
    assetcache_update_ptr(arg0, sp1C);
    return sp1C;
}

void *func_802558D8(void *arg0, void *arg1){
    void *v1;
    v1 = func_80255888(arg0);
    if(v1 == arg0){
        return arg0;
    }
    else{
        D_802765A8 = arg1;
        return v1;
    }
}

bool func_80255920(void *arg0) {
    HeapHeader *block;

    if ((arg0 == NULL) || (arg0 == D_8027659C) || (D_802765A0 != NULL)) {
        return FALSE;
    }

    block = &((HeapHeader*)arg0)[-1];
    return (block->prev->unkC_7 != HEAP_BLOCK_EMPTY ) ? FALSE : TRUE;
}

HeapHeader * func_80255978(void *ptr){
    return ((HeapHeader* )((s32)ptr - sizeof(HeapHeader)))->prev;
}

void func_80255980(void *arg0, int arg1){
    D_802765A0 = arg0;
    D_802765A4 = D_802765AC;
}

bool func_802559A0(void) {
    return (D_80276598 == 0) ? (D_80283220 >= 0xF4240) || ((D_80276594 == 1) ? 0 : 1) : 0;
}

void func_80255A04(void){
    D_80276594 = 1;
}

void func_80255A14(void){
    D_80276594 = 0;
}

void func_80255A20(void){
    D_80276598 = TRUE;
}

void func_80255A30(void){
    D_80276598 = FALSE;
}

void func_80255A3C(void){
    func_80255524();
    if(func_802559A0() && D_80276598 != TRUE)
        return;
    
    if(!func_802559A0())
        func_802E6820(1);

    if(!func_802559A0())
        printbuffer_defrag();

    if(!func_802559A0()){
        ml_defrag();
        dummy_func_80254464();
    }

}

void func_80255ACC(void){
    D_802765AC++;
}

bool func_80255AE4(void){
    return (D_802765A0) ? 1 : 0; 
}

int func_80255B08(int arg0){
    if(arg0 == 2) 
        return 0x3;
    return 0x12;
}
