/*---------------------------------------------------------------------------*
  Project:  NitroSDK - WFS - libraries
  File:     wfs_format.c

  Copyright 2007-2008 Nintendo. All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law. They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

 *---------------------------------------------------------------------------*/


#include <nitro/wfs/format.h>


/*---------------------------------------------------------------------------*/
/* Functions */

/*---------------------------------------------------------------------------*
  Name:         WFS_LoadTable

  Description:  Reads the NTR binary FAT from the device.

  Arguments:    archive: The WFSTableFormat structure to initialize
                allocator: The allocator used to internally allocate memory
                device: The device storing the target FAT.
                origin_a: The device-internal offset for the ROM loading the source FAT.
                origin_b: The device-internal offset for the ROM that merges the overlay.

  Returns:      TRUE if the FAT is loaded correctly.
 *---------------------------------------------------------------------------*/
BOOL WFS_LoadTable(WFSTableFormat *archive, MIAllocator *allocator,
                   MIDevice *device, u32 origin_a, u32 origin_b)
{
    const u32       diff = origin_b - origin_a;
    const BOOL      mixed = (diff != 0);
    u32             mixed_overlay_count = 0;
    u32             origin_b_fat = 0;
    CARDRomRegion   mem_region[WFS_TABLE_REGION_MAX];
    int             i;

    /* Initializes the structure */
    archive->origin = origin_a;
    archive->buffer = NULL;
    archive->length = 0;

    /* Load the A header and record it in fat/fnt */
    {
        u8                      buf[0x60];
        const CARDRomHeader    *header = (const CARDRomHeader *)buf;
        (void)MI_ReadDevice(device, buf, archive->origin, sizeof(buf));
        archive->region[WFS_TABLE_REGION_FAT].offset = MI_LoadLE32(&header->fat.offset);
        archive->region[WFS_TABLE_REGION_FAT].length = MI_LoadLE32(&header->fat.length);
        archive->region[WFS_TABLE_REGION_FNT].offset = MI_LoadLE32(&header->fnt.offset);
        archive->region[WFS_TABLE_REGION_FNT].length = MI_LoadLE32(&header->fnt.length);
    }
    /* Load the B header and record it in ov9/ov7 */
    {
        u8                      buf[0x60];
        const CARDRomHeader    *header = (const CARDRomHeader *)buf;
        (void)MI_ReadDevice(device, buf, origin_b, sizeof(buf));
        archive->region[WFS_TABLE_REGION_OV9].offset = MI_LoadLE32(&header->main_ovt.offset) + diff;
        archive->region[WFS_TABLE_REGION_OV9].length = MI_LoadLE32(&header->main_ovt.length);
        archive->region[WFS_TABLE_REGION_OV7].offset = MI_LoadLE32(&header->sub_ovt.offset) + diff;
        archive->region[WFS_TABLE_REGION_OV7].length = MI_LoadLE32(&header->sub_ovt.length);
        origin_b_fat = header->fat.offset + diff;
    }
    /* If 'mixed', [fat += (ov9 + ov7)] */
    if (mixed)
    {
        mixed_overlay_count = (archive->region[WFS_TABLE_REGION_OV9].length +
                               archive->region[WFS_TABLE_REGION_OV7].length)
                              / sizeof(WFSOVLFormat);
    }
    /* Calculate length */
    {
        u32 offset = 0;
        offset += sizeof(u32);
        for (i = 0; i < WFS_TABLE_REGION_MAX; ++i)
        {
            mem_region[i].offset = offset;
            mem_region[i].length = archive->region[i].length;
            if (i == WFS_TABLE_REGION_FAT)
            {
                mem_region[i].length += mixed_overlay_count * sizeof(WFSFATFormat);
            }
            mem_region[i].length = MATH_ROUNDUP(mem_region[i].length, 32);
            offset += sizeof(u32) + mem_region[i].length;
        }
        archive->length = offset;
    }
    /* Allocate the buffer */
    if (allocator)
    {
        archive->buffer = MI_CallAlloc(allocator, archive->length, sizeof(u32));
        if (archive->buffer)
        {
            /* Load either A or B's fat/fnt/ov9/ov7. NOTE: origin has been adjusted. */
            {
                u8     *dst = archive->buffer;
                MI_StoreLE32(dst, archive->origin);
                dst += sizeof(u32);
                for (i = 0; i < WFS_TABLE_REGION_MAX; ++i)
                {
                    const u32 length = archive->region[i].length;
                    const u32 padding = mem_region[i].length - length;
                    MI_StoreLE32(dst, length);
                    dst += sizeof(u32);
                    (void)MI_ReadDevice(device, dst, archive->origin + archive->region[i].offset, length);
                    dst += length;
                    MI_CpuFill8(dst, 0x00, padding);
                    dst += padding;
                }
            }
            /* If 'mixed', [fat += (ov9 + ov7)] */
            if (mixed)
            {
                u32     count = archive->region[WFS_TABLE_REGION_FAT].length / sizeof(WFSFATFormat);
                u32     total = count + mixed_overlay_count;
                u8     *dst = &archive->buffer[mem_region[WFS_TABLE_REGION_FAT].offset];
                WFSFATFormat *fat = (WFSFATFormat*)(dst + sizeof(u32));
                MI_StoreLE32(dst, total * sizeof(WFSFATFormat));
                for (i = WFS_TABLE_REGION_OV9; i < WFS_TABLE_REGION_MAX; ++i)
                {
                    WFSOVLFormat *ovl = (WFSOVLFormat *)&archive->buffer[mem_region[i].offset + sizeof(u32)];
                    u32     num = archive->region[i].length / sizeof(WFSOVLFormat);
                    u32     j;
                    for (j = 0; j < num; ++j)
                    {
                        if (count >= total)
                        {
                            OS_TWarning("exceeded FAT iterator! (internal logic error)");
                        }
                        else
                        {
                            WFSFATFormat entry;
                            (void)MI_ReadDevice(device, &entry,
                                                archive->origin + origin_b_fat + ovl[j].file_id * sizeof(WFSFATFormat),
                                                sizeof(entry));
                            MI_StoreLE32(&fat[count].top, MI_LEToH32(entry.top) + diff);
                            MI_StoreLE32(&fat[count].bottom, MI_LEToH32(entry.bottom) + diff);
                            MI_StoreLE32(&ovl[j].file_id, count);
                            ++count;
                        }
                    }

                }
            }
        }
    }
    return (archive->buffer != NULL);
}

/*---------------------------------------------------------------------------*
  Name:         WFS_ParseTable

  Description:  Parses the memory that read the NTR binary FAT image.

  Arguments:    archive: The WFSTableFormat structure to initialize
                                 The FAT image must be configured in advance in the members 'buffer' and 'length'.
                                 

  Returns:      None.
 *---------------------------------------------------------------------------*/
void WFS_ParseTable(WFSTableFormat *archive)
{
    const u8   *src = archive->buffer;
    u32         pos = 0;
    int         i;
    /* Get the base offset */
    archive->origin = MI_LoadLE32(&src[pos]);
    pos += sizeof(u32);
    /* Extract information about each region from the given buffer */
    for (i = 0; i < WFS_TABLE_REGION_MAX; ++i)
    {
        u32     len = MI_LoadLE32(&src[pos]);
        pos += sizeof(u32);
        archive->region[i].offset = pos;
        archive->region[i].length = len;
        pos += MATH_ROUNDUP(len, 32);
        if (pos > archive->length)
        {
            OS_TPanic("specified invalid archive buffer!");
        }
    }
}


/*---------------------------------------------------------------------------*
  $Log: wfs_format.c,v $
  Revision 1.1  2007/04/13 04:12:37  yosizaki
  Initial upload.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/
