diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/user/gim.h | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/include/user/gim.h b/include/user/gim.h new file mode 100644 index 0000000..2d83431 --- /dev/null +++ b/include/user/gim.h @@ -0,0 +1,365 @@ +#ifndef _DOLCESDK_PSP2_GIM_H_ +#define _DOLCESDK_PSP2_GIM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <psp2/gxm.h> +#include <psp2/scebase.h> + +#include <psp2/kernel/clib.h> +#include <psp2/kernel/sysmem.h> + +#define SCE_GIM_TAG 0x2E47494DUL /* 'GIM.' */ +#define SCE_GIM_VERSION 0x312E3030UL /* '1.00' */ + +/** GIM data block types */ +#define SCE_GIM_BLOCK_TYPE_ROOT 0x02 +#define SCE_GIM_BLOCK_TYPE_PICTURE 0x03 +#define SCE_GIM_BLOCK_TYPE_IMAGE 0x04 +#define SCE_GIM_BLOCK_TYPE_PALETTE 0x05 +#define SCE_GIM_BLOCK_TYPE_FILEINFO 0xFF + +/** GIM texture data formats */ +#define SCE_GIM_TYPE_RGBA5650 0x0000 +#define SCE_GIM_TYPE_RGBA5551 0x0001 +#define SCE_GIM_TYPE_RGBA4444 0x0002 +#define SCE_GIM_TYPE_RGBA8888 0x0003 +#define SCE_GIM_TYPE_INDEX4 0x0004 +#define SCE_GIM_TYPE_INDEX8 0x0005 + +/** GIM error codes */ +typedef int SceGimErrorCode; +#define SCE_GIM_ERROR_INVALID_VALUE -1 +#define SCE_GIM_ERROR_INVALID_POINTER -2 +#define SCE_GIM_ERROR_INVALID_ALIGNMENT -3 +#define SCE_GIM_ERROR_NOT_FOUND -4 + +#define SCE_GIM_PALETTE_SIZE_P4 64U +#define SCE_GIM_PALETTE_SIZE_P8 1024U + +#define _SCE_GIM_ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) + +/** A header for a GIM file. + This structure should be located right at the start of the GIM file. +*/ +typedef struct SceGimHeader { + uint32_t tag; //!< GIM Identifier + uint32_t version; //!< Version number + uint32_t formatStyle; //!< GIM file format + uint32_t pad; //!< Padding +} SceGimHeader; + +/* Contains information about data block in the GIM file. */ +typedef struct SceGimBlockInfo { + uint16_t blockId; //!< Block id (level) + uint16_t unk; //!< Always 0 on PSP2 + uint32_t blockSize; //!< Size of this block + children blocks + uint32_t blockHeaderNext; //!< Offset to the next block header + uint32_t blockDataOffset; //!< Offset to the data of this block +} SceGimBlockInfo; + +/* Contains information about texture block in the GIM file. */ +typedef struct SceGimTextureInfo { + uint16_t blockHeaderSize; //!< Size of this header (0x30) + uint16_t reference; //!< Always 0 on PSP2 + uint16_t format; //!< Texture format + uint16_t pixelOrder; //!< Always 0 (normal order) on PSP2 + uint16_t width; //!< Texture width + uint16_t height; //!< Texture height + uint16_t bppAlign; //!< Image/palette alignment BPP + uint16_t pitchAlign; //!< Image/palette alignment X + uint16_t heightAlign; //!< Image/palette alignment Y + uint16_t dimCount; //!< Always 2 + uint16_t reserved; //!< Always 0 + uint16_t reserved2; //!< Always 0 + uint32_t indexStart; //!< Index relative start offset + uint32_t pixelsStart; //!< First plane/level/frame relative start offset + uint32_t pixelsEnd; //!< Last plane/level/frame relative end offset + uint32_t planeMask; //!< Always 0 on PSP2 + uint16_t levelType; + uint16_t levelCount; //!< Number of mipmaps + uint16_t frameType; //!< Always 3 + uint16_t frameCount; //!< Always 1 on PSP2 (static images) + uint32_t levelOffset; //!< Always 0x40 on PSP2 + uint8_t pad[12]; //!< Padding +} SceGimTextureInfo; + +#ifndef SCE_GIM_ERROR_RETURN +#define SCE_GIM_ERROR_RETURN(COND, ERROR_CODE, FMT, ...) \ + do { \ + if (!(COND)) { return (ERROR_CODE); } \ + } while (false) +#endif + +#ifndef SCE_GIM_ASSERT +#define SCE_GIM_ASSERT(x) ((void)(x)) +#endif + +/// Check if a value is aligned to the given power of 2 alignment. +#define SCE_GIM_IS_ALIGNED(VALUE, ALIGNMENT) (((uint32_t)(VALUE) & ((ALIGNMENT) - 1)) == 0) + +/** Gets the header size of a GIM file. + @return The size of the header in bytes. +*/ +inline uint32_t sceGimGetHeaderSize(void) { + return sizeof(SceGimHeader); +} + +/** Gets the start address of the texture data within a GIM file. + @param gim A pointer to the GIM data. + @return A pointer to the start of the texture data. +*/ +inline void* sceGimGetDataAddress(void *gim) { + return (void*)((uint32_t)gim + sceGimGetHeaderSize()); +} + +/** Gets the size of the texture data within a GIM file. + @param gim A pointer to the GIM data. + @return The size of the texture data in bytes. +*/ +inline uint32_t sceGimGetDataSize(void *gim) { + void* data_hdr = sceGimGetDataAddress(gim); + SceGimBlockInfo *hdr = (SceGimBlockInfo*)data_hdr; + return hdr->blockSize; +} + +/** Gets the size of the whole GIM file. + @param gim A pointer to the GIM data. + @return The size of the file in bytes. +*/ +inline uint32_t sceGimGetSize(void *gim) { + return sceGimGetDataSize(gim) + sceGimGetHeaderSize(); +} + +/** Gets address of the image block. + @param gim A pointer to the GIM data. + @return A pointer to the start of the image block. +*/ +inline void* sceGimFindImageAddress(void *gim) { + + uint32_t nextHdrOffs = sceGimGetHeaderSize(); + uint32_t gimSize = sceGimGetSize(gim); + SceGimBlockInfo *hdr; + + while (nextHdrOffs < gimSize) { + hdr = (SceGimBlockInfo*)((uint32_t)gim + nextHdrOffs); + + if (hdr->blockId == SCE_GIM_BLOCK_TYPE_IMAGE) + return (void *)hdr; + + nextHdrOffs = hdr->blockHeaderNext + nextHdrOffs; + } + + return NULL; +} + +/** Gets address of the palette block. + @param gim A pointer to the GIM data. + @return A pointer to the start of the palette block. +*/ +inline void* sceGimFindPaletteAddress(void *gim) { + + uint32_t nextHdrOffs = sceGimGetHeaderSize(); + uint32_t gimSize = sceGimGetSize(gim); + SceGimBlockInfo *hdr; + + while (nextHdrOffs < gimSize) { + hdr = (SceGimBlockInfo*)((uint32_t)gim + nextHdrOffs); + + if (hdr->blockId == SCE_GIM_BLOCK_TYPE_PALETTE) + return (void *)hdr; + + nextHdrOffs = hdr->blockHeaderNext + nextHdrOffs; + } + + return NULL; +} + +/** Gets the number of textures in a GIM file. + @return The number of textures. +*/ +inline uint32_t sceGimGetTextureCount(void *gim) { + return 1; +} + +/** Initializes the texture control words for a given GIM texture. + + @param texture A pointer to texture to be initialized. + @param gim A pointer to the GIM data. + + @retval + SCE_OK The operation was successful. + @retval + SCE_GIM_ERROR_INVALID_ALIGNMENT The operation failed due to an invalid buffer alignment. + @retval + SCE_GIM_ERROR_INVALID_VALUE The operation failed due to an invalid input parameter. + @retval + SCE_GIM_ERROR_INVALID_POINTER The operation failed due to an invalid input pointer. +*/ +inline SceGimErrorCode sceGimInitTexture(SceGxmTexture *texture, void *gim) { + + SceGimTextureInfo *imgHdr = NULL; + SceGimHeader *hdr = (SceGimHeader*)gim; + + // check parameters + SCE_GIM_ERROR_RETURN( + texture, + SCE_GIM_ERROR_INVALID_POINTER, + ""); + SCE_GIM_ERROR_RETURN( + gim, + SCE_GIM_ERROR_INVALID_POINTER, + ""); + + void* img = sceGimFindImageAddress(gim); + + SCE_GIM_ERROR_RETURN( + img, + SCE_GIM_ERROR_NOT_FOUND, + ""); + + void* pal = sceGimFindPaletteAddress(gim); + + if (pal) + pal = (void*)((uint32_t)pal + 0x50); + + imgHdr = (SceGimTextureInfo*)((uint32_t)img + 0x10); + img = (void*)((uint32_t)img + 0x50); + + SceGxmTextureFormat texFormat; + + if(hdr->version == SCE_GIM_VERSION) { + switch(imgHdr->format) + { + case SCE_GIM_TYPE_INDEX8: + texFormat = SCE_GXM_TEXTURE_FORMAT_P8_ABGR; + break; + case SCE_GIM_TYPE_INDEX4: + texFormat = SCE_GXM_TEXTURE_FORMAT_P4_ABGR; + break; + case SCE_GIM_TYPE_RGBA8888: + texFormat = SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR; + break; + case SCE_GIM_TYPE_RGBA4444: + texFormat = SCE_GXM_TEXTURE_FORMAT_U4U4U4U4_ABGR; + break; + case SCE_GIM_TYPE_RGBA5551: + texFormat = SCE_GXM_TEXTURE_FORMAT_U1U5U5U5_ABGR; + break; + case SCE_GIM_TYPE_RGBA5650: + texFormat = SCE_GXM_TEXTURE_FORMAT_U5U6U5_BGR; + break; + default: + // Unsupported type + SCE_GIM_ASSERT(0); + } + } + else { + SCE_GIM_ERROR_RETURN( + false, + SCE_GIM_ERROR_INVALID_VALUE, + ""); + } + + SceGxmErrorCode res = SCE_OK; + + int32_t align = imgHdr->pitchAlign * 8 / imgHdr->bppAlign - 1; + int32_t strideWidth = (imgHdr->width + align) & ~align; + + res = sceGxmTextureInitLinear( + texture, + img, + texFormat, + strideWidth, + imgHdr->height, + imgHdr->levelCount); + + if (res < 0) { + return res; + } + + if (pal) { + uint32_t pal_size = 0; + + if (imgHdr->format == SCE_GIM_TYPE_INDEX8) { + pal_size = SCE_GIM_PALETTE_SIZE_P8; + } + else if (imgHdr->format == SCE_GIM_TYPE_INDEX4) { + pal_size = SCE_GIM_PALETTE_SIZE_P4; + } + + pal_size = _SCE_GIM_ALIGN(pal_size, 4 * 1024); + + SceUID id = sceKernelAllocMemBlock("gpu_mem", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, pal_size, NULL); + + if (id < 0) { + return id; + } + + void* pal_al; + + sceKernelGetMemBlockBase(id, &pal_al); + + res = sceGxmMapMemory(pal_al, pal_size, SCE_GXM_MEMORY_ATTRIB_READ); + + if (res < 0) { + return res; + } + + if (imgHdr->format == SCE_GIM_TYPE_INDEX8) { + sceClibMemcpy(pal_al, pal, SCE_GIM_PALETTE_SIZE_P8); + } + else if (imgHdr->format == SCE_GIM_TYPE_INDEX4) { + sceClibMemcpy(pal_al, pal, SCE_GIM_PALETTE_SIZE_P4); + } + + res = sceGxmTextureSetPalette(texture, pal_al); + } + + return res; +} + +/** Checks if a pointer looks like a GIM file. + + @param gim A pointer to the GIM data. + + @retval + SCE_OK The data structure passes GIM header validation. + @retval + SCE_GIM_ERROR_INVALID_VALUE The operation failed because the header magic number was invalid + or the version numbers are not compatible. + @retval + SCE_GIM_ERROR_INVALID_POINTER The operation failed due to an invalid input pointer. +*/ +inline SceGimErrorCode sceGimCheckData(void *gim) { + // check parameter + SCE_GIM_ERROR_RETURN( + gim, + SCE_GIM_ERROR_INVALID_POINTER, + ""); + + // check the magic sequence + SCE_GIM_ERROR_RETURN( + ((SceGimHeader*)gim)->tag == SCE_GIM_TAG, + SCE_GIM_ERROR_INVALID_VALUE, + ""); + + // check the header version + SCE_GIM_ERROR_RETURN( + ((SceGimHeader*)gim)->version == SCE_GIM_VERSION, + SCE_GIM_ERROR_INVALID_VALUE, + ""); + + // all ok + return SCE_OK; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DOLCESDK_PSP2_GIM_H_ */ + |