EBOOT Title

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

EBOOT Title

Post by Ooblik »

Does anyone know how to get the EBOOT Title from an EBOOT. I've searched and found code to get the ICON0.PNG and modified it to load the image into memory and display with a modded graphics.h but I cannot figure out how to get the title. Preferably a way without having to decompress and parse the PARAM.SFO...

My goal is to do it quicker than the XMB.
Onii
Posts: 40
Joined: Sun Oct 05, 2008 1:07 pm

Post by Onii »

It's apparently not in the BPB itself.

http://hitmen.c02.at/files/yapspd/psp_d ... ml#sec26.3

The next section in the same doc shows the SFO info, which is where I think your going to have to go, even though you said you didn't want to go there.

You can try to build a hello world type app and pack it onto a PBP, open it with a hex editor and search for the title you gave it to see if there's any possibility at all.
roe-ur-boat
Posts: 5
Joined: Wed Aug 22, 2007 4:54 am

Post by roe-ur-boat »

AFAIK the only way to get the eboot name is by parsing the sfo that's packed in with the rest of the files in the pbp.

Edit: With pbps compiled with the latest toolchain I think the name is at the same offset in every file. You could get it by seeking to that offset and just reading it till you reach a NULL byte. However, this method isn't as accurate as parsing it properly.
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

Post by Ooblik »

You can get the actual offset from the SFO pretty easily. I still have no idea how to unpack the SFO. Any one got any code?
roe-ur-boat
Posts: 5
Joined: Wed Aug 22, 2007 4:54 am

Post by roe-ur-boat »

It's not compressed, all the files in the pbp are just joined together.
ne0h
Posts: 386
Joined: Thu Feb 21, 2008 2:15 am

Post by ne0h »

Ooblik, cau you post the code for get the ICON0.PNG data?
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

Post by Ooblik »

roe-ur-boat wrote:It's not compressed, all the files in the pbp are just joined together.
Unpack.. unjoin.. whatever I still need to do it...?

ne0h wrote:Ooblik, cau you post the code for get the ICON0.PNG data?
Can you post code to get Title data? :D

Here you go...

Remember most of the code is NOT mine just modified by me.
Thank the people in these forums:D ..Living (well not living..) proof that search works!

Take a look at: extern Image* GetEBOOTIcon(const char * path);
and extern Image* loadImageMemory(const void *buffer, int bufsize);

The code may be a little messy or have some errors.. compiles fine though :)

graphics.h

Code: Select all

#ifndef GRAPHICS_H
#define GRAPHICS_H

#include <psptypes.h>

#define PSP_LINE_SIZE 512
#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 272

typedef u32 Color;
#define A&#40;color&#41; &#40;&#40;u8&#41;&#40;color >> 24 & 0xFF&#41;&#41;
#define B&#40;color&#41; &#40;&#40;u8&#41;&#40;color >> 16 & 0xFF&#41;&#41;
#define G&#40;color&#41; &#40;&#40;u8&#41;&#40;color >> 8 & 0xFF&#41;&#41;
#define R&#40;color&#41; &#40;&#40;u8&#41;&#40;color & 0xFF&#41;&#41;

#define RGB&#40;r, g, b&#41; &#40;0xFF000000 | &#40;&#40;b&#41;<<16&#41; | &#40;&#40;g&#41;<<8&#41; | &#40;r&#41;&#41;

enum colors &#123;
RED = 0xFF0000FF,
GREEN = 0xFF00FF00,
BLUE = 0xFFFF0000,
WHITE = 0xFFFFFFFF,
LITEGRAY = 0xFFBFBFBF,
GRAY = 0xFF7F7F7F,
DARKGRAY = 0xFF3F3F3F	,	
BLACK = 0xFF000000,
YELLOW = 0xFF00FFFF,
ORANGE = 0xFF0080FF,
LITEORANGE = 0xFF88C4FF,
VANILLA = 0xFFA8F7FB,
PURPLE = 0xFFCE00CE,
DARKRED = 0xFF000080,
PINK = 0xFFCD00CD
&#125;;

typedef struct
&#123;
	
	int		textureWidth;  // the real width of data, 2^n with n>=0
	int		textureHeight;  // the real height of data, 2^n with n>=0
	int		imageWidth;  // the image width
	int		imageHeight;
	Color* 	data;

&#125; Image;

typedef struct 
&#123; 
	u32 signature; 
	int version; 
	int offset&#91;8&#93;; 
&#125; HEADER;

extern Image* loadImageMemory&#40;const void *buffer, int bufsize&#41;;
extern Image* GetEBOOTIcon&#40;const char * path&#41;;

/**
 * Load a PNG image.
 *
 * @pre filename != NULL
 * @param filename - filename of the PNG image to load
 * @return pointer to a new allocated Image struct, or NULL on failure
 */
extern Image* loadImage&#40;const char* filename&#41;;

extern Image* loadJpegImage&#40;const char* filename, int offset&#41;;

void PutGFX&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy&#41;;
/**
 * Blit a rectangle part of an image to another image.
 *
 * @pre source != NULL && destination != NULL &&
 *      sx >= 0 && sy >= 0 &&
 *      width > 0 && height > 0 &&
 *      sx + width <= source->width && sy + height <= source->height &&
 *      dx + width <= destination->width && dy + height <= destination->height
 * @param sx - left position of rectangle in source image
 * @param sy - top position of rectangle in source image
 * @param width - width of rectangle in source image
 * @param height - height of rectangle in source image
 * @param source - pointer to Image struct of the source image
 * @param dx - left target position in destination image
 * @param dy - top target position in destination image
 * @param destination - pointer to Image struct of the destination image
 */
extern void blitImageToImage&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy, Image* destination&#41;;

/**
 * Blit a rectangle part of an image to screen.
 *
 * @pre source != NULL && destination != NULL &&
 *      sx >= 0 && sy >= 0 &&
 *      width > 0 && height > 0 &&
 *      sx + width <= source->width && sy + height <= source->height &&
 *      dx + width <= SCREEN_WIDTH && dy + height <= SCREEN_HEIGHT
 * @param sx - left position of rectangle in source image
 * @param sy - top position of rectangle in source image
 * @param width - width of rectangle in source image
 * @param height - height of rectangle in source image
 * @param source - pointer to Image struct of the source image
 * @param dx - left target position in destination image
 * @param dy - top target position in destination image
 */
extern void blitImageToScreen&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy&#41;;

/**
 * Blit a rectangle part of an image to another image without alpha pixels in source image.
 *
 * @pre source != NULL && destination != NULL &&
 *      sx >= 0 && sy >= 0 &&
 *      width > 0 && height > 0 &&
 *      sx + width <= source->width && sy + height <= source->height &&
 *      dx + width <= destination->width && dy + height <= destination->height
 * @param sx - left position of rectangle in source image
 * @param sy - top position of rectangle in source image
 * @param width - width of rectangle in source image
 * @param height - height of rectangle in source image
 * @param source - pointer to Image struct of the source image
 * @param dx - left target position in destination image
 * @param dy - top target position in destination image
 * @param destination - pointer to Image struct of the destination image
 */
extern void blitAlphaImageToImage&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy, Image* destination&#41;;

/**
 * Blit a rectangle part of an image to screen without alpha pixels in source image.
 *
 * @pre source != NULL && destination != NULL &&
 *      sx >= 0 && sy >= 0 &&
 *      width > 0 && height > 0 &&
 *      sx + width <= source->width && sy + height <= source->height &&
 *      dx + width <= SCREEN_WIDTH && dy + height <= SCREEN_HEIGHT
 * @param sx - left position of rectangle in source image
 * @param sy - top position of rectangle in source image
 * @param width - width of rectangle in source image
 * @param height - height of rectangle in source image
 * @param source - pointer to Image struct of the source image
 * @param dx - left target position in destination image
 * @param dy - top target position in destination image
 */
extern void blitAlphaImageToScreen&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy&#41;;

/**
 * Create an empty image.
 *
 * @pre width > 0 && height > 0 && width <= 512 && height <= 512
 * @param width - width of the new image
 * @param height - height of the new image
 * @return pointer to a new allocated Image struct, all pixels initialized to color 0, or NULL on failure
 */
extern Image* createImage&#40;int width, int height&#41;;

/**
 * Frees an allocated image.
 *
 * @pre image != null
 * @param image a pointer to an image struct
 */
extern void freeImage&#40;Image* image&#41;;

/**
 * Initialize all pixels of an image with a color.
 *
 * @pre image != NULL
 * @param color - new color for the pixels
 * @param image - image to clear
 */
extern void clearImage&#40;Color color, Image* image&#41;;

/**
 * Initialize all pixels of the screen with a color.
 *
 * @param color - new color for the pixels
 */
extern void clearScreen&#40;Color color&#41;;

/**
 * Fill a rectangle of an image with a color.
 *
 * @pre image != NULL
 * @param color - new color for the pixels
 * @param x0 - left position of rectangle in image
 * @param y0 - top position of rectangle in image
 * @param width - width of rectangle in image
 * @param height - height of rectangle in image
 * @param image - image
 */
extern void fillImageRect&#40;Color color, int x0, int y0, int width, int height, Image* image&#41;;

/**
 * Fill a rectangle of an image with a color.
 *
 * @pre image != NULL
 * @param color - new color for the pixels
 * @param x0 - left position of rectangle in image
 * @param y0 - top position of rectangle in image
 * @param width - width of rectangle in image
 * @param height - height of rectangle in image
 */
extern void fillScreenRect&#40;Color color, int x0, int y0, int width, int height&#41;;

/**
 * Set a pixel on screen to the specified color.
 *
 * @pre x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT
 * @param color - new color for the pixels
 * @param x - left position of the pixel
 * @param y - top position of the pixel
 */
extern void putPixelScreen&#40;Color color, int x, int y&#41;;

/**
 * Set a pixel in an image to the specified color.
 *
 * @pre x >= 0 && x < image->imageWidth && y >= 0 && y < image->imageHeight && image != NULL
 * @param color - new color for the pixels
 * @param x - left position of the pixel
 * @param y - top position of the pixel
 */
extern void putPixelImage&#40;Color color, int x, int y, Image* image&#41;;

/**
 * Get the color of a pixel on screen.
 *
 * @pre x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT
 * @param x - left position of the pixel
 * @param y - top position of the pixel
 * @return the color of the pixel
 */
extern Color getPixelScreen&#40;int x, int y&#41;;

/**
 * Get the color of a pixel of an image.
 *
 * @pre x >= 0 && x < image->imageWidth && y >= 0 && y < image->imageHeight && image != NULL
 * @param x - left position of the pixel
 * @param y - top position of the pixel
 * @return the color of the pixel
 */
extern Color getPixelImage&#40;int x, int y, Image* image&#41;;

/**
 * Print a text &#40;pixels out of the screen or image are clipped&#41;.
 *
 * @param x - left position of text
 * @param y - top position of text
 * @param text - the text to print
 * @param color - new color for the pixels
 */
extern void printTextScreen&#40;int x, int y, const char* text, u32 color&#41;;
extern void printTextCenterScreen&#40;int x, int y, const char* text, u32 color&#41;;

/**
 * Print a text &#40;pixels out of the screen or image are clipped&#41;.
 *
 * @param x - left position of text
 * @param y - top position of text
 * @param text - the text to print
 * @param color - new color for the pixels
 * @param image - image
 */
extern void printTextImage&#40;int x, int y, const char* text, u32 color, Image* image&#41;;

/**
 * Save an image or the screen in PNG format.
 *
 * @pre filename != NULL
 * @param filename - filename of the PNG image
 * @param data - start of Color type pixel data &#40;can be getVramDisplayBuffer&#40;&#41;&#41;
 * @param width - logical width of the image or SCREEN_WIDTH
 * @param height - height of the image or SCREEN_HEIGHT
 * @param lineSize - physical width of the image or PSP_LINE_SIZE
 * @param saveAlpha - if 0, image is saved without alpha channel
 */
extern void saveImage&#40;const char* filename, Color* data, int width, int height, int lineSize, int saveAlpha&#41;;

/**
 * Exchange display buffer and drawing buffer.
 */
extern void flipScreen&#40;&#41;;

/**
 * Initialize the graphics.
 */
extern void initGraphics&#40;&#41;;

/**
 * Disable graphics, used for debug text output.
 */
extern void disableGraphics&#40;&#41;;

/**
 * Draw a line to screen.
 *
 * @pre x0 >= 0 && x0 < SCREEN_WIDTH && y0 >= 0 && y0 < SCREEN_HEIGHT &&
 *      x1 >= 0 && x1 < SCREEN_WIDTH && y1 >= 0 && y1 < SCREEN_HEIGHT
 * @param x0 - x line start position
 * @param y0 - y line start position
 * @param x1 - x line end position
 * @param y1 - y line end position
 */
void drawLineScreen&#40;int x0, int y0, int x1, int y1, Color color&#41;;

/**
 * Draw a line to screen.
 *
 * @pre x0 >= 0 && x0 < image->imageWidth && y0 >= 0 && y0 < image->imageHeight &&
 *      x1 >= 0 && x1 < image->imageWidth && y1 >= 0 && y1 < image->imageHeight
 * @param x0 - x line start position
 * @param y0 - y line start position
 * @param x1 - x line end position
 * @param y1 - y line end position
 */
extern void drawLineImage&#40;int x0, int y0, int x1, int y1, Color color, Image* image&#41;;

/**
 * Get the current draw buffer for fast unchecked access.
 *
 * @return the start address of the current draw buffer
 */
extern Color* getVramDrawBuffer&#40;&#41;;

/**
 * Get the current display buffer for fast unchecked access.
 *
 * @return the start address of the current display buffer
 */
extern Color* getVramDisplayBuffer&#40;&#41;;

extern void guStart&#40;&#41;;
extern void guEnd&#40;&#41;;

#endif
graphics.c

Code: Select all

#include <stdlib.h>
#include <malloc.h>
#include <pspdisplay.h>
#include <psputils.h>
#include <png.h>
#include <pspgu.h>

#include <jpeglib.h>
#include <jerror.h>

#include "graphics.h"
#include "framebuffer.h"

#define IS_ALPHA&#40;color&#41; &#40;&#40;&#40;color&#41;&0xff000000&#41;==0xff000000?0&#58;1&#41;
#define FRAMEBUFFER_SIZE &#40;PSP_LINE_SIZE*SCREEN_HEIGHT*4&#41;
#define MAX&#40;X, Y&#41; &#40;&#40;X&#41; > &#40;Y&#41; ? &#40;X&#41; &#58; &#40;Y&#41;&#41;

typedef struct 
&#123; 

    const unsigned char *buffer; 
    png_uint_32 bufsize; 
    png_uint_32 current_pos; 

&#125; MEMORY_READER_STATE; 

typedef struct
&#123;
	unsigned short u, v;
	short x, y, z;
&#125; Vertex;

extern u8 msx&#91;&#93;;

unsigned int __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; list&#91;262144&#93;;
static int dispBufferNumber;
static int initialized = 0;

static int getNextPower2&#40;int width&#41;
&#123;
	int b = width;
	int n;
	for &#40;n = 0; b != 0; n++&#41; b >>= 1;
	b = 1 << n;
	if &#40;b == 2 * width&#41; b >>= 1;
	return b;
&#125;

Color* getVramDrawBuffer&#40;&#41;
&#123;
	Color* vram = &#40;Color*&#41; g_vram_base;
	if &#40;dispBufferNumber == 0&#41; vram += FRAMEBUFFER_SIZE / sizeof&#40;Color&#41;;
	return vram;
&#125;

Color* getVramDisplayBuffer&#40;&#41;
&#123;
	Color* vram = &#40;Color*&#41; g_vram_base;
	if &#40;dispBufferNumber == 1&#41; vram += FRAMEBUFFER_SIZE / sizeof&#40;Color&#41;;
	return vram;
&#125;

void user_warning_fn&#40;png_structp png_ptr, png_const_charp warning_msg&#41;
&#123;
&#125;

static void read_data_memory&#40;png_structp png_ptr, png_bytep data, png_uint_32 length&#41; 
&#123; 
   MEMORY_READER_STATE *f = &#40;MEMORY_READER_STATE *&#41;png_get_io_ptr&#40;png_ptr&#41;; 
   if &#40;length > &#40;f->bufsize - f->current_pos&#41;&#41; png_error&#40;png_ptr, "read error in read_data_memory &#40;loadpng&#41;"&#41;; 
   memcpy&#40;data, f->buffer + f->current_pos, length&#41;; 
   f->current_pos += length; 
&#125; 

Image* loadImageMemory&#40;const void *buffer, int bufsize&#41; 
&#123; 
   png_structp png_ptr; 
   png_infop info_ptr; 
   MEMORY_READER_STATE memory_reader_state; 
   unsigned int sig_read = 0; 
   png_uint_32 width, height; 
   int bit_depth, color_type, interlace_type, x, y; 
   u32* line; 
    
   if &#40;!buffer || &#40;bufsize <= 0&#41;&#41; return NULL; 

   Image* image = &#40;Image*&#41; malloc&#40;sizeof&#40;Image&#41;&#41;; 
   if &#40;!image&#41; return NULL; 

   png_ptr = png_create_read_struct&#40;PNG_LIBPNG_VER_STRING, NULL, NULL, NULL&#41;; 
   if &#40;png_ptr == NULL&#41; &#123; 
      free&#40;image&#41;; 
      return NULL;; 
   &#125; 

   png_set_error_fn&#40;png_ptr, &#40;png_voidp&#41; NULL, &#40;png_error_ptr&#41; NULL, user_warning_fn&#41;; 

   info_ptr = png_create_info_struct&#40;png_ptr&#41;; 
   if &#40;info_ptr == NULL&#41; &#123; 
      free&#40;image&#41;; 
      png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;; 
      return NULL; 
   &#125; 

   memory_reader_state.buffer = &#40;unsigned char *&#41;buffer; 
   memory_reader_state.bufsize = bufsize; 
   memory_reader_state.current_pos = 0; 

   png_set_read_fn&#40;png_ptr, &memory_reader_state, &#40;png_rw_ptr&#41;read_data_memory&#41;; 
   png_read_info&#40;png_ptr, info_ptr&#41;; 
   png_get_IHDR&#40;png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL&#41;; 

   image->imageWidth = width; 
   image->imageHeight = height; 
   image->textureWidth = getNextPower2&#40;width&#41;; 
   image->textureHeight = getNextPower2&#40;height&#41;; 

   png_set_sig_bytes&#40;png_ptr, sig_read&#41;; 
   png_set_strip_16&#40;png_ptr&#41;; 
   png_set_packing&#40;png_ptr&#41;; 

   if &#40;color_type == PNG_COLOR_TYPE_PALETTE&#41; png_set_palette_to_rgb&#40;png_ptr&#41;; 
   if &#40;color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8&#41; png_set_gray_1_2_4_to_8&#40;png_ptr&#41;; 
   if &#40;png_get_valid&#40;png_ptr, info_ptr, PNG_INFO_tRNS&#41;&#41; png_set_tRNS_to_alpha&#40;png_ptr&#41;; 
   png_set_filler&#40;png_ptr, 0xff, PNG_FILLER_AFTER&#41;; 

   image->data = &#40;Color*&#41; memalign&#40;16, image->textureWidth * image->textureHeight * sizeof&#40;Color&#41;&#41;; 
   if &#40;!image->data&#41; &#123; 
      free&#40;image&#41;; 
      png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;; 
      return NULL; 
   &#125; 
   line = &#40;u32*&#41; malloc&#40;width * 4&#41;; 
   if &#40;!line&#41; &#123; 
      free&#40;image->data&#41;; 
      free&#40;image&#41;; 
      png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;; 
      return NULL; 
   &#125; 
   for &#40;y = 0; y < height; y++&#41; &#123; 
      png_read_row&#40;png_ptr, &#40;u8*&#41; line, png_bytep_NULL&#41;; 
      for &#40;x = 0; x < width; x++&#41; &#123; 
         u32 color = line&#91;x&#93;; 
         image->data&#91;x + y * image->textureWidth&#93; =  color; 
      &#125; 
   &#125; 

   free&#40;line&#41;; 
   png_read_end&#40;png_ptr, info_ptr&#41;; 
   png_destroy_read_struct&#40;&png_ptr, &info_ptr, png_infopp_NULL&#41;; 

   return image; 
&#125; 

Image* loadImage&#40;const char* filename&#41;
&#123;
	png_structp png_ptr;
	png_infop info_ptr;
	unsigned int sig_read = 0;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type, x, y;
	u32* line;
	FILE *fp;
	Image* image = &#40;Image*&#41; malloc&#40;sizeof&#40;Image&#41;&#41;;
	if &#40;!image&#41; return NULL;

	if &#40;&#40;fp = fopen&#40;filename, "rb"&#41;&#41; == NULL&#41; return NULL;
	png_ptr = png_create_read_struct&#40;PNG_LIBPNG_VER_STRING, NULL, NULL, NULL&#41;;
	if &#40;png_ptr == NULL&#41; &#123;
		free&#40;image&#41;;
		fclose&#40;fp&#41;;
		return NULL;;
	&#125;
	png_set_error_fn&#40;png_ptr, &#40;png_voidp&#41; NULL, &#40;png_error_ptr&#41; NULL, user_warning_fn&#41;;
	info_ptr = png_create_info_struct&#40;png_ptr&#41;;
	if &#40;info_ptr == NULL&#41; &#123;
		free&#40;image&#41;;
		fclose&#40;fp&#41;;
		png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;;
		return NULL;
	&#125;
	png_init_io&#40;png_ptr, fp&#41;;
	png_set_sig_bytes&#40;png_ptr, sig_read&#41;;
	png_read_info&#40;png_ptr, info_ptr&#41;;
	png_get_IHDR&#40;png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL&#41;;
	/*
	if &#40;width > 512 || height > 512&#41; &#123;
		free&#40;image&#41;;
		fclose&#40;fp&#41;;
		png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;;
		return NULL;
	&#125;
	*/
	image->imageWidth = width;
	image->imageHeight = height;
	image->textureWidth = getNextPower2&#40;width&#41;;
	image->textureHeight = getNextPower2&#40;height&#41;;
	png_set_strip_16&#40;png_ptr&#41;;
	png_set_packing&#40;png_ptr&#41;;
	if &#40;color_type == PNG_COLOR_TYPE_PALETTE&#41; png_set_palette_to_rgb&#40;png_ptr&#41;;
	if &#40;color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8&#41; png_set_gray_1_2_4_to_8&#40;png_ptr&#41;;
	if &#40;png_get_valid&#40;png_ptr, info_ptr, PNG_INFO_tRNS&#41;&#41; png_set_tRNS_to_alpha&#40;png_ptr&#41;;
	png_set_filler&#40;png_ptr, 0xff, PNG_FILLER_AFTER&#41;;
	image->data = &#40;Color*&#41; memalign&#40;16, image->textureWidth * image->textureHeight * sizeof&#40;Color&#41;&#41;;
	if &#40;!image->data&#41; &#123;
		free&#40;image&#41;;
		fclose&#40;fp&#41;;
		png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;;
		return NULL;
	&#125;
	line = &#40;u32*&#41; malloc&#40;width * 4&#41;;
	if &#40;!line&#41; &#123;
		free&#40;image->data&#41;;
		free&#40;image&#41;;
		fclose&#40;fp&#41;;
		png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;;
		return NULL;
	&#125;
	for &#40;y = 0; y < height; y++&#41; &#123;
		png_read_row&#40;png_ptr, &#40;u8*&#41; line, png_bytep_NULL&#41;;
		for &#40;x = 0; x < width; x++&#41; &#123;
			u32 color = line&#91;x&#93;;
			image->data&#91;x + y * image->textureWidth&#93; =  color;
		&#125;
	&#125;
	free&#40;line&#41;;
	png_read_end&#40;png_ptr, info_ptr&#41;;
	png_destroy_read_struct&#40;&png_ptr, &info_ptr, png_infopp_NULL&#41;;
	fclose&#40;fp&#41;;
	return image;
&#125;

Image* loadJpegImage&#40;const char* filename, int offset&#41;
&#123;
	int x;
	struct jpeg_decompress_struct dinfo;
	struct jpeg_error_mgr jerr;
	dinfo.err = jpeg_std_error&#40;&jerr&#41;;
	jpeg_create_decompress&#40;&dinfo&#41;;
	FILE* inFile = fopen&#40;filename, "rb"&#41;;
		if &#40;!inFile&#41; &#123;
		jpeg_destroy_decompress&#40;&dinfo&#41;;
		return NULL;
		&#125;	
	fseek&#40;inFile, offset, SEEK_SET&#41;;	
	jpeg_stdio_src&#40;&dinfo, inFile&#41;;
	jpeg_read_header&#40;&dinfo, TRUE&#41;;
	int width = dinfo.image_width;
	int height = dinfo.image_height;
	jpeg_start_decompress&#40;&dinfo&#41;;
	Image* image = &#40;Image*&#41; malloc&#40;sizeof&#40;Image&#41;&#41;;
	if &#40;!image&#41; &#123;
		jpeg_destroy_decompress&#40;&dinfo&#41;;
		fclose&#40;inFile&#41;;
		return NULL;
	&#125;
	image->imageWidth = width;
	image->imageHeight = height;
	image->textureWidth = getNextPower2&#40;width&#41;;
	image->textureHeight = getNextPower2&#40;height&#41;;
	image->data = &#40;Color*&#41; memalign&#40;16, image->textureWidth * image->textureHeight * sizeof&#40;Color&#41;&#41;;
	u8* line = &#40;u8*&#41; malloc&#40;width * 3&#41;;
	if &#40;!line&#41; &#123;
		jpeg_destroy_decompress&#40;&dinfo&#41;;
		fclose&#40;inFile&#41;;
		return NULL;
	&#125;
	if &#40;dinfo.jpeg_color_space == JCS_GRAYSCALE&#41; &#123;
		while &#40;dinfo.output_scanline < dinfo.output_height&#41; &#123;
			int y = dinfo.output_scanline;
			jpeg_read_scanlines&#40;&dinfo, &line, 1&#41;;
			for &#40;x = 0; x < width; x++&#41; &#123;
				Color c = line&#91;x&#93;;
				image->data&#91;x + image->textureWidth * y&#93; = c | &#40;c << 8&#41; | &#40;c << 16&#41; | 0xff000000;;
			&#125;
		&#125;
	&#125; else &#123;
		while &#40;dinfo.output_scanline < dinfo.output_height&#41; &#123;
			int y = dinfo.output_scanline;
			jpeg_read_scanlines&#40;&dinfo, &line, 1&#41;;
			u8* linePointer = line;
			for &#40;x = 0; x < width; x++&#41; &#123;
				Color c = *&#40;linePointer++&#41;;
				c |= &#40;*&#40;linePointer++&#41;&#41; << 8;
				c |= &#40;*&#40;linePointer++&#41;&#41; << 16;
				image->data&#91;x + image->textureWidth * y&#93; = c | 0xff000000;
			&#125;
		&#125;
	&#125;
	jpeg_finish_decompress&#40;&dinfo&#41;;
	jpeg_destroy_decompress&#40;&dinfo&#41;;
	free&#40;line&#41;;
	fclose&#40;inFile&#41;;
	return image;
&#125;

void PutGFX&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy&#41;
&#123;
	if &#40;!initialized&#41; return;

	sceKernelDcacheWritebackInvalidateAll&#40;&#41;;
	guStart&#40;&#41;;
	sceGuTexImage&#40;0, source->textureWidth, source->textureHeight, source->textureWidth, &#40;void*&#41; source->data&#41;;
	float u = 1.0f / &#40;&#40;float&#41;source->textureWidth&#41;;
	float v = 1.0f / &#40;&#40;float&#41;source->textureHeight&#41;;
	sceGuTexScale&#40;u, v&#41;;

	int j = 0;
	while &#40;j < width&#41; &#123;
		Vertex* vertices = &#40;Vertex*&#41; sceGuGetMemory&#40;2 * sizeof&#40;Vertex&#41;&#41;;
		int sliceWidth = 64;
		if &#40;j + sliceWidth > width&#41; sliceWidth = width - j;
		vertices&#91;0&#93;.u = sx + j;
		vertices&#91;0&#93;.v = sy;
		vertices&#91;0&#93;.x = dx + j;
		vertices&#91;0&#93;.y = dy;
		vertices&#91;0&#93;.z = 0;
		vertices&#91;1&#93;.u = sx + j + sliceWidth;
		vertices&#91;1&#93;.v = sy + height;
		vertices&#91;1&#93;.x = dx + j + sliceWidth;
		vertices&#91;1&#93;.y = dy + height;
		vertices&#91;1&#93;.z = 0;
		sceGuDrawArray&#40;GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices&#41;;
		j += sliceWidth;
	&#125;

	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0, 0&#41;;
&#125;

void blitImageToImage&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy, Image* destination&#41;
&#123;
	Color* destinationData = &destination->data&#91;destination->textureWidth * dy + dx&#93;;
	int destinationSkipX = destination->textureWidth - width;
	Color* sourceData = &source->data&#91;source->textureWidth * sy + sx&#93;;
	int sourceSkipX = source->textureWidth - width;
	int x, y;
	for &#40;y = 0; y < height; y++, destinationData += destinationSkipX, sourceData += sourceSkipX&#41; &#123;
		for &#40;x = 0; x < width; x++, destinationData++, sourceData++&#41; &#123;
			*destinationData = *sourceData;
		&#125;
	&#125;
&#125;

void blitImageToScreen&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy&#41;
&#123;
	if &#40;!initialized&#41; return;
	Color* vram = getVramDrawBuffer&#40;&#41;;
	sceKernelDcacheWritebackInvalidateAll&#40;&#41;;
	guStart&#40;&#41;;
	sceGuCopyImage&#40;GU_PSM_8888, sx, sy, width, height, source->textureWidth, source->data, dx, dy, PSP_LINE_SIZE, vram&#41;;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0,0&#41;;
&#125;

void blitAlphaImageToImage&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy, Image* destination&#41;
&#123;
	// TODO Blend!
	Color* destinationData = &destination->data&#91;destination->textureWidth * dy + dx&#93;;
	int destinationSkipX = destination->textureWidth - width;
	Color* sourceData = &source->data&#91;source->textureWidth * sy + sx&#93;;
	int sourceSkipX = source->textureWidth - width;
	int x, y;
	for &#40;y = 0; y < height; y++, destinationData += destinationSkipX, sourceData += sourceSkipX&#41; &#123;
		for &#40;x = 0; x < width; x++, destinationData++, sourceData++&#41; &#123;
			Color color = *sourceData;
			if &#40;!IS_ALPHA&#40;color&#41;&#41; *destinationData = color;
		&#125;
	&#125;
&#125;

void blitAlphaImageToScreen&#40;int sx, int sy, int width, int height, Image* source, int dx, int dy&#41;
&#123;
	if &#40;!initialized&#41; return;

	sceKernelDcacheWritebackInvalidateAll&#40;&#41;;
	guStart&#40;&#41;;
	sceGuTexImage&#40;0, source->textureWidth, source->textureHeight, source->textureWidth, &#40;void*&#41; source->data&#41;;
	float u = 1.0f / &#40;&#40;float&#41;source->textureWidth&#41;;
	float v = 1.0f / &#40;&#40;float&#41;source->textureHeight&#41;;
	sceGuTexScale&#40;u, v&#41;;
	
	int j = 0;
	while &#40;j < width&#41; &#123;
		Vertex* vertices = &#40;Vertex*&#41; sceGuGetMemory&#40;2 * sizeof&#40;Vertex&#41;&#41;;
		int sliceWidth = 64;
		if &#40;j + sliceWidth > width&#41; sliceWidth = width - j;
		vertices&#91;0&#93;.u = sx + j;
		vertices&#91;0&#93;.v = sy;
		vertices&#91;0&#93;.x = dx + j;
		vertices&#91;0&#93;.y = dy;
		vertices&#91;0&#93;.z = 0;
		vertices&#91;1&#93;.u = sx + j + sliceWidth;
		vertices&#91;1&#93;.v = sy + height;
		vertices&#91;1&#93;.x = dx + j + sliceWidth;
		vertices&#91;1&#93;.y = dy + height;
		vertices&#91;1&#93;.z = 0;
		sceGuDrawArray&#40;GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices&#41;;
		j += sliceWidth;
	&#125;
	
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0, 0&#41;;
&#125;

Image* createImage&#40;int width, int height&#41;
&#123;
	Image* image = &#40;Image*&#41; malloc&#40;sizeof&#40;Image&#41;&#41;;
	if &#40;!image&#41; return NULL;
	image->imageWidth = width;
	image->imageHeight = height;
	image->textureWidth = getNextPower2&#40;width&#41;;
	image->textureHeight = getNextPower2&#40;height&#41;;
	image->data = &#40;Color*&#41; memalign&#40;16, image->textureWidth * image->textureHeight * sizeof&#40;Color&#41;&#41;;
	if &#40;!image->data&#41; return NULL;
	memset&#40;image->data, 0, image->textureWidth * image->textureHeight * sizeof&#40;Color&#41;&#41;;
	return image;
&#125;

void freeImage&#40;Image* image&#41;
&#123;
	free&#40;image->data&#41;;
	free&#40;image&#41;;
&#125;

void clearImage&#40;Color color, Image* image&#41;
&#123;
	int i;
	int size = image->textureWidth * image->textureHeight;
	Color* data = image->data;
	for &#40;i = 0; i < size; i++, data++&#41; *data = color;
&#125;

void clearScreen&#40;Color color&#41;
&#123;
	if &#40;!initialized&#41; return;
	guStart&#40;&#41;;
	sceGuClearColor&#40;color&#41;;
	sceGuClearDepth&#40;0&#41;;
	sceGuClear&#40;GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT&#41;;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0, 0&#41;;
&#125;

void fillImageRect&#40;Color color, int x0, int y0, int width, int height, Image* image&#41;
&#123;
	int skipX = image->textureWidth - width;
	int x, y;
	Color* data = image->data + x0 + y0 * image->textureWidth;
	for &#40;y = 0; y < height; y++, data += skipX&#41; &#123;
		for &#40;x = 0; x < width; x++, data++&#41; *data = color;
	&#125;
&#125;

void fillScreenRect&#40;Color color, int x0, int y0, int width, int height&#41;
&#123;
	if &#40;!initialized&#41; return;
	int skipX = PSP_LINE_SIZE - width;
	int x, y;
	Color* data = getVramDrawBuffer&#40;&#41; + x0 + y0 * PSP_LINE_SIZE;
	for &#40;y = 0; y < height; y++, data += skipX&#41; &#123;
		for &#40;x = 0; x < width; x++, data++&#41; *data = color;
	&#125;
&#125;

void putPixelScreen&#40;Color color, int x, int y&#41;
&#123;
	Color* vram = getVramDrawBuffer&#40;&#41;;
	vram&#91;PSP_LINE_SIZE * y + x&#93; = color;
&#125;

void putPixelImage&#40;Color color, int x, int y, Image* image&#41;
&#123;
	image->data&#91;x + y * image->textureWidth&#93; = color;
&#125;

Color getPixelScreen&#40;int x, int y&#41;
&#123;
	Color* vram = getVramDrawBuffer&#40;&#41;;
	return vram&#91;PSP_LINE_SIZE * y + x&#93;;
&#125;

Color getPixelImage&#40;int x, int y, Image* image&#41;
&#123;
	return image->data&#91;x + y * image->textureWidth&#93;;
&#125;

void printTextScreen&#40;int x, int y, const char* text, u32 color&#41;
&#123;
	int c, i, j, l;
	u8 *font;
	Color *vram_ptr;
	Color *vram;
	
	if &#40;!initialized&#41; return;

	for &#40;c = 0; c < strlen&#40;text&#41;; c++&#41; &#123;
		if &#40;x < 0 || x + 8 > SCREEN_WIDTH || y < 0 || y + 8 > SCREEN_HEIGHT&#41; break;
		char ch = text&#91;c&#93;;
		vram = getVramDrawBuffer&#40;&#41; + x + y * PSP_LINE_SIZE;
		
		font = &msx&#91; &#40;int&#41;ch * 8&#93;;
		for &#40;i = l = 0; i < 8; i++, l += 8, font++&#41; &#123;
			vram_ptr  = vram;
			for &#40;j = 0; j < 8; j++&#41; &#123;
				if &#40;&#40;*font & &#40;128 >> j&#41;&#41;&#41; *vram_ptr = color;
				vram_ptr++;
			&#125;
			vram += PSP_LINE_SIZE;
		&#125;
		x += 8;
	&#125;
&#125;

void printTextImage&#40;int x, int y, const char* text, u32 color, Image* image&#41;
&#123;
	int c, i, j, l;
	u8 *font;
	Color *data_ptr;
	Color *data;
	
	if &#40;!initialized&#41; return;

	for &#40;c = 0; c < strlen&#40;text&#41;; c++&#41; &#123;
		if &#40;x < 0 || x + 8 > image->imageWidth || y < 0 || y + 8 > image->imageHeight&#41; break;
		char ch = text&#91;c&#93;;
		data = image->data + x + y * image->textureWidth;
		
		font = &msx&#91; &#40;int&#41;ch * 8&#93;;
		for &#40;i = l = 0; i < 8; i++, l += 8, font++&#41; &#123;
			data_ptr  = data;
			for &#40;j = 0; j < 8; j++&#41; &#123;
				if &#40;&#40;*font & &#40;128 >> j&#41;&#41;&#41; *data_ptr = color;
				data_ptr++;
			&#125;
			data += image->textureWidth;
		&#125;
		x += 8;
	&#125;
&#125;

void saveImage&#40;const char* filename, Color* data, int width, int height, int lineSize, int saveAlpha&#41;
&#123;
	png_structp png_ptr;
	png_infop info_ptr;
	FILE* fp;
	int i, x, y;
	u8* line;
	
	if &#40;&#40;fp = fopen&#40;filename, "wb"&#41;&#41; == NULL&#41; return;
	png_ptr = png_create_write_struct&#40;PNG_LIBPNG_VER_STRING, NULL, NULL, NULL&#41;;
	if &#40;!png_ptr&#41; return;
	info_ptr = png_create_info_struct&#40;png_ptr&#41;;
	if &#40;!info_ptr&#41; &#123;
		png_destroy_write_struct&#40;&png_ptr, &#40;png_infopp&#41;NULL&#41;;
		return;
	&#125;
	png_init_io&#40;png_ptr, fp&#41;;
	png_set_IHDR&#40;png_ptr, info_ptr, width, height, 8,
		saveAlpha ? PNG_COLOR_TYPE_RGBA &#58; PNG_COLOR_TYPE_RGB,
		PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT&#41;;
	png_write_info&#40;png_ptr, info_ptr&#41;;
	line = &#40;u8*&#41; malloc&#40;width * &#40;saveAlpha ? 4 &#58; 3&#41;&#41;;
	for &#40;y = 0; y < height; y++&#41; &#123;
		for &#40;i = 0, x = 0; x < width; x++&#41; &#123;
			Color color = data&#91;x + y * lineSize&#93;;
			u8 r = color & 0xff; 
			u8 g = &#40;color >> 8&#41; & 0xff;
			u8 b = &#40;color >> 16&#41; & 0xff;
			u8 a = saveAlpha ? &#40;color >> 24&#41; & 0xff &#58; 0xff;
			line&#91;i++&#93; = r;
			line&#91;i++&#93; = g;
			line&#91;i++&#93; = b;
			if &#40;saveAlpha&#41; line&#91;i++&#93; = a;
		&#125;
		png_write_row&#40;png_ptr, line&#41;;
	&#125;
	free&#40;line&#41;;
	png_write_end&#40;png_ptr, info_ptr&#41;;
	png_destroy_write_struct&#40;&png_ptr, &#40;png_infopp&#41;NULL&#41;;
	fclose&#40;fp&#41;;
&#125;

void flipScreen&#40;&#41;
&#123;
	if &#40;!initialized&#41; return;
	sceGuSwapBuffers&#40;&#41;;
	dispBufferNumber ^= 1;
&#125;

static void drawLine&#40;int x0, int y0, int x1, int y1, int color, Color* destination, int width&#41;
&#123;
	int dy = y1 - y0;
	int dx = x1 - x0;
	int stepx, stepy;
	
	if &#40;dy < 0&#41; &#123; dy = -dy;  stepy = -width; &#125; else &#123; stepy = width; &#125;
	if &#40;dx < 0&#41; &#123; dx = -dx;  stepx = -1; &#125; else &#123; stepx = 1; &#125;
	dy <<= 1;
	dx <<= 1;
	
	y0 *= width;
	y1 *= width;
	destination&#91;x0+y0&#93; = color;
	if &#40;dx > dy&#41; &#123;
		int fraction = dy - &#40;dx >> 1&#41;;
		while &#40;x0 != x1&#41; &#123;
			if &#40;fraction >= 0&#41; &#123;
				y0 += stepy;
				fraction -= dx;
			&#125;
			x0 += stepx;
			fraction += dy;
			destination&#91;x0+y0&#93; = color;
		&#125;
	&#125; else &#123;
		int fraction = dx - &#40;dy >> 1&#41;;
		while &#40;y0 != y1&#41; &#123;
			if &#40;fraction >= 0&#41; &#123;
				x0 += stepx;
				fraction -= dy;
			&#125;
			y0 += stepy;
			fraction += dx;
			destination&#91;x0+y0&#93; = color;
		&#125;
	&#125;
&#125;

void drawLineScreen&#40;int x0, int y0, int x1, int y1, Color color&#41;
&#123;
	drawLine&#40;x0, y0, x1, y1, color, getVramDrawBuffer&#40;&#41;, PSP_LINE_SIZE&#41;;
&#125;

void drawLineImage&#40;int x0, int y0, int x1, int y1, Color color, Image* image&#41;
&#123;
	drawLine&#40;x0, y0, x1, y1, color, image->data, image->textureWidth&#41;;
&#125;

#define BUF_WIDTH &#40;512&#41;
#define SCR_WIDTH &#40;480&#41;
#define SCR_HEIGHT &#40;272&#41;
#define PIXEL_SIZE &#40;4&#41; /* change this if you change to another screenmode */
#define FRAME_SIZE &#40;BUF_WIDTH * SCR_HEIGHT * PIXEL_SIZE&#41;
#define ZBUF_SIZE &#40;BUF_WIDTH SCR_HEIGHT * 2&#41; /* zbuffer seems to be 16-bit? */

void initGraphics&#40;&#41;
&#123;
	dispBufferNumber = 0;

	sceGuInit&#40;&#41;;

	guStart&#40;&#41;;
	sceGuDrawBuffer&#40;GU_PSM_8888, &#40;void*&#41;FRAMEBUFFER_SIZE, PSP_LINE_SIZE&#41;;
	sceGuDispBuffer&#40;SCREEN_WIDTH, SCREEN_HEIGHT, &#40;void*&#41;0, PSP_LINE_SIZE&#41;;
	sceGuClear&#40;GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT&#41;;
	sceGuDepthBuffer&#40;&#40;void*&#41; &#40;FRAMEBUFFER_SIZE*2&#41;, PSP_LINE_SIZE&#41;;
	sceGuOffset&#40;2048 - &#40;SCREEN_WIDTH / 2&#41;, 2048 - &#40;SCREEN_HEIGHT / 2&#41;&#41;;
	sceGuViewport&#40;2048, 2048, SCREEN_WIDTH, SCREEN_HEIGHT&#41;;
	sceGuDepthRange&#40;0xc350, 0x2710&#41;;
	sceGuScissor&#40;0, 0, SCREEN_WIDTH, SCREEN_HEIGHT&#41;;
	sceGuEnable&#40;GU_SCISSOR_TEST&#41;;
	sceGuAlphaFunc&#40;GU_GREATER, 0, 0xff&#41;;
	sceGuEnable&#40;GU_ALPHA_TEST&#41;;
	sceGuDepthFunc&#40;GU_GEQUAL&#41;;
	sceGuEnable&#40;GU_DEPTH_TEST&#41;;
	sceGuFrontFace&#40;GU_CW&#41;;
	sceGuShadeModel&#40;GU_SMOOTH&#41;;
	sceGuEnable&#40;GU_CULL_FACE&#41;;
	sceGuEnable&#40;GU_TEXTURE_2D&#41;;
	sceGuEnable&#40;GU_CLIP_PLANES&#41;;
	sceGuTexMode&#40;GU_PSM_8888, 0, 0, 0&#41;;
	sceGuTexFunc&#40;GU_TFX_REPLACE, GU_TCC_RGBA&#41;;
	sceGuTexFilter&#40;GU_NEAREST, GU_NEAREST&#41;;
	sceGuAmbientColor&#40;0xffffffff&#41;;
	sceGuEnable&#40;GU_BLEND&#41;;
	sceGuBlendFunc&#40;GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0&#41;;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0, 0&#41;;

	sceDisplayWaitVblankStart&#40;&#41;;
	sceGuDisplay&#40;GU_TRUE&#41;;
	initialized = 1;
&#125;

void disableGraphics&#40;&#41;
&#123;
	initialized = 0;
&#125;

void guStart&#40;&#41;
&#123;
	sceGuStart&#40;GU_DIRECT, list&#41;;
&#125;

void guEnd&#40;void&#41; &#123;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0, 0&#41;;
	guStart&#40;&#41;;
	sceGuTexMode&#40;GU_PSM_8888, 0, 0, 0&#41;;
	sceGuTexFunc&#40;GU_TFX_REPLACE, GU_TCC_RGBA&#41;;
	sceGuTexFilter&#40;GU_NEAREST, GU_NEAREST&#41;;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0, 0&#41;;
&#125;

void printTextCenterScreen&#40;int x, int y, const char* text, u32 color&#41;
&#123;
	 printTextScreen&#40;x - &#40;&#40;strlen&#40;text&#41; * 8&#41; / 2&#41;, y, text, color&#41;;
&#125;

Image* GetEBOOTIcon&#40;const char * path&#41; &#123; 

   Image* imgSource = NULL; 
    
   int loop0; 
   FILE *infile; 
   HEADER header; 

   infile = fopen&#40;path, "rb"&#41;; 

   fseek&#40;infile, 0, SEEK_SET&#41;;

   if &#40;fread&#40;&header, sizeof&#40;HEADER&#41;, 1, infile&#41; < 0&#41; &#123; return NULL; &#125; 

   if &#40;header.signature != 0x50425000&#41; return NULL;

   for &#40;loop0=0; loop0<2; loop0++&#41; &#123; 

      void *buffer; int size; 
        
      size = header.offset&#91;loop0 + 1&#93; - header.offset&#91;loop0&#93;; 

      buffer = malloc&#40;size&#41;; 
      if &#40;buffer == NULL&#41; &#123; return NULL; &#125; 

      if &#40;fread&#40;buffer, size, 1, infile&#41; < 0&#41; &#123; return NULL; &#125; 

      if &#40;loop0==1&#41; &#123; 
			imgSource = loadImageMemory&#40;buffer, size&#41;; 
      &#125; 
    
      free&#40;buffer&#41;; 
   &#125; 

   fclose&#40;infile&#41;;

   if &#40;!imgSource&#41; &#123; 
         imgSource = createImage&#40;144, 80&#41;; //Or point to another image...
   &#125; 

   return &#40;imgSource&#41;; 
&#125;
Insert_witty_name
Posts: 376
Joined: Wed May 10, 2006 11:31 pm

Post by Insert_witty_name »

In samples/utility/gamesharing I manually override the title in the PARAM.SFO to 'PSPSDK GameShare'.

I would recommend doing it the correct way as others have mentioned.
Onii
Posts: 40
Joined: Sun Oct 05, 2008 1:07 pm

Post by Onii »

Something I hacked up to get the title from the sfo. And when I say hacked I mean it.

It's all based on the sfo info at the link I gave above. Since sfo files are so small I don't know if this way or loading the whole thing into memory and going off array offsets would be faster, but it would not be hard to convert this code to that method. Maybe if I have time tomorrow night I'll try.

The only thing that's REMOTELY interesting here in terms of speed of finding the title; we look throught the key / value pairs backwards since the doc says that they are alpha sorted, and we bail our loop early if it's not a type 2 value (TXT).

2/3 of it is error code, and if you feel lucky you can chop most out. I only tested it on 2 sfo's since that's all I had handy. It should be trivial to mod it to read an eboot.

EDIT: maybe comments tomorrow night too, it's late.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int getTitle&#40;const char * fileName, char * gameTitle&#41;&#123;
    int keyTableOffset;
    int valueTableOffset;

    short keyOffset;
    char keyType;
    char keyTest&#91;6&#93;;

    int valueLen;
    int valueOffset;

    int numberOfPairs;

    int curPairLoc;

    int i;

    FILE * fp = fopen&#40;fileName, "rb"&#41;;
    
    if&#40;!fp&#41;&#123;
        return -1;
    &#125;

    if&#40;fseek&#40;fp, 8, SEEK_SET&#41; != 0&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    if&#40;fread&#40;&keyTableOffset, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    if&#40;fread&#40;&valueTableOffset, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    if&#40;fread&#40;&numberOfPairs, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    for&#40;i = numberOfPairs - 1; i >= 0; i--&#41;&#123;

        if&#40;fseek&#40;fp, &#40;16 * i&#41; + 20, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fread&#40;&keyOffset, 2, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fseek&#40;fp, 1, SEEK_CUR&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fread&#40;&keyType, 1, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;keyType != 2&#41;&#123;
            continue;
        &#125;

        curPairLoc = ftell&#40;fp&#41;;

        if&#40;fseek&#40;fp, keyTableOffset + keyOffset, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fread&#40;&keyTest, 1, 6, fp&#41; != 6&#41;&#123;
            continue;
        &#125;

        if&#40;strcmp&#40;"TITLE", keyTest&#41; != 0&#41;&#123;
            continue;
        &#125;

        if&#40;fseek&#40;fp, curPairLoc, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fseek&#40;fp, 4, SEEK_CUR&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fread&#40;&valueLen, 4, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fread&#40;&valueOffset, 4, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fseek&#40;fp, valueTableOffset + valueOffset, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        if&#40;fread&#40;gameTitle, 1, valueLen, fp&#41; != valueLen&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;;

        fclose&#40;fp&#41;;
        return 1;
    &#125;

    fclose&#40;fp&#41;;

    strcpy&#40;gameTitle, "Title not found."&#41;;

    return 0;
&#125;

int main&#40;int argc,  char* argv&#91;&#93;&#41;&#123;
    char * gameName = malloc&#40;400&#41;;

    if&#40;getTitle&#40;"PARAM.SFO", gameName&#41; > 0&#41;&#123;
        printf&#40;"%s\n", gameName&#41;;
    &#125;else&#123;
        printf&#40;"Error\n"&#41;;
    &#125;

    free&#40;gameName&#41;;
&#125;
ne0h
Posts: 386
Joined: Thu Feb 21, 2008 2:15 am

Post by ne0h »

Thanks, but I've found another library to get the icon and I use a personal library for loading images, but this is not the problem!
Anyway thanks!
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

Post by Ooblik »

Code: Select all

	
int fd = sceIoOpen&#40;"EBOOT.PBP", PSP_O_RDONLY, 0777&#41;;

	params.datasize = sceIoLseek32&#40;fd, 0, PSP_SEEK_END&#41;;
	
	unsigned char *fileBuffer = memalign&#40;64, params.datasize&#41;;

	sceIoLseek32&#40;fd, 0, PSP_SEEK_SET&#41;;

	sceIoRead&#40;fd, fileBuffer, params.datasize&#41;;
	
	sceIoClose&#40;fd&#41;;
	
	// Manually patch the PARAM.SFO in the EBOOT
	fileBuffer&#91;276&#93; = 0x57;
	// And add a custom filename
	strncpy&#40;&#40;char *&#41; &fileBuffer&#91;320&#93;, "PSPSDK GameShare", 127&#41;;

	memcpy&#40;&params.name, "GameShar", 8&#41;;
This changes the title. Now how do I get it? It's probably very simple..
BTW Thanks Insert_witty_name
Onii
Posts: 40
Joined: Sun Oct 05, 2008 1:07 pm

Post by Onii »

Umm that's a really bad way to set the title. That only works on homebrews and completely ignores the SFO header data. If your new title is longer than the old one it will write over your terminating 0 byte and stomp over the next piece of data in the sfo. (though since it's not written back to the file, if that's the only thing you use the filebuffer for, it's okay)

I'll modify my code in a few minutes to read the title reliably, not just from homebrew eboots.
Last edited by Onii on Wed Oct 08, 2008 11:58 am, edited 1 time in total.
Onii
Posts: 40
Joined: Sun Oct 05, 2008 1:07 pm

Post by Onii »

Here's the version that works on eboots. This also has much less memory overhead than the snip you posted, as it does not load the whole eboot into memory. I also added comments so it's more transparent. It should be trivial to switch it over to use the sceIO functions if you want to do that. Let me know if you'd like a version that has less reads and seeks. I can mod it to just load the SFO data into a temp buffer and just use memory offsets over a char array. That method may be even faster. Just say the word and I'll be happy to write that version too.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int getTitle&#40;const char * fileName, char * gameTitle&#41;&#123;
    int keyTableOffset;
    int valueTableOffset;
    int SFOOffset;

    short keyOffset;
    char keyType;
    char keyTest&#91;6&#93;;

    int valueLen;
    int valueOffset;

    int numberOfPairs;

    int curPairLoc;

    int i;

    FILE * fp = fopen&#40;fileName, "rb"&#41;;
    
    if&#40;!fp&#41;&#123;
        return -1;
    &#125;

    // Skip the EBOOT header
    //
    if&#40;fseek&#40;fp, 8, SEEK_SET&#41; != 0&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    // Read the offset to the sfo data in the eboot
    //
    if&#40;fread&#40;&SFOOffset, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    // Skip to the sfo data
    //
    if&#40;fseek&#40;fp, SFOOffset + 8, SEEK_SET&#41; != 0&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    // Read the offset to the keytable in the sfo data
    //
    if&#40;fread&#40;&keyTableOffset, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    // Normalize the keytable to the EBOOT
    //
    keyTableOffset += SFOOffset;

    // Read the offset to the valuetable in the sfo data
    //
    if&#40;fread&#40;&valueTableOffset, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    // Normalize the valuetable to the EBOOT
    //
    valueTableOffset += SFOOffset;

    // Read the number of value / key pairs present in the sfo
    //
    if&#40;fread&#40;&numberOfPairs, 4, 1, fp&#41; != 1&#41;&#123;
        fclose&#40;fp&#41;;
        return -1;
    &#125;

    // Set up a backwards loop to iterate over the key value index
    //
    for&#40;i = numberOfPairs - 1; i >= 0; i--&#41;&#123;

        // Skip to the current &#40;EBOOT normalized&#41; keyvalue index
        //
        if&#40;fseek&#40;fp, SFOOffset + &#40;16 * i&#41; + 20, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Read key offset &#40;normalized to the keytable&#41; from the current key / value index
        //
        if&#40;fread&#40;&keyOffset, 2, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Skip the unknown byte &#40;might be an alignment indicator&#41;
        //
        if&#40;fseek&#40;fp, 1, SEEK_CUR&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Read key type
        //
        if&#40;fread&#40;&keyType, 1, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // If the key type is not 2 &#40;TXT&#41; this key / value pair cant be the title so continue to the next index
        //
        if&#40;keyType != 2&#41;&#123;
            continue;
        &#125;

        // Since we will need to jump out of the index and go look at the actual data, record where we were.
        //
        curPairLoc = ftell&#40;fp&#41;;

        // Skip to the key in the keytable
        //
        if&#40;fseek&#40;fp, keyTableOffset + keyOffset, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Read the key into memory &#40;we only read 6 bytes because we are specifically looking for TITLE\0&#41;
        // if we can't read the whole 6 bytes, move on to the next index
        //
        if&#40;fread&#40;&keyTest, 1, 6, fp&#41; != 6&#41;&#123;
            continue;
        &#125;

        // Compare the current key to "TITLE"
        // if it doesn't match, move on to the next index
        //
        if&#40;strcmp&#40;"TITLE", keyTest&#41; != 0&#41;&#123;
            continue;
        &#125;

        // If we're here, we have the proper key, skip back to our index
        //
        if&#40;fseek&#40;fp, curPairLoc, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Skip the raw data length in the index
        //
        if&#40;fseek&#40;fp, 4, SEEK_CUR&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Read the length of the data and the padding
        //
        if&#40;fread&#40;&valueLen, 4, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Read the offset of the actual value normalized to the value table
        //
        if&#40;fread&#40;&valueOffset, 4, 1, fp&#41; != 1&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Skip to the actual value
        //
        if&#40;fseek&#40;fp, valueTableOffset + valueOffset, SEEK_SET&#41; != 0&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;

        // Read the value from the value table into the char array that was passed into the function
        //
        if&#40;fread&#40;gameTitle, 1, valueLen, fp&#41; != valueLen&#41;&#123;
            fclose&#40;fp&#41;;
            return -1;
        &#125;;

        // Close the EBOOT
        //
        fclose&#40;fp&#41;;

        // Return success
        //
        return 1;
    &#125;

    // If we're here we never found a key that matched TITLE
    //
    fclose&#40;fp&#41;;

    // Return a catch all string
    //
    strcpy&#40;gameTitle, "Title not found."&#41;;

    // Return failure
    //
    return 0;
&#125;

int main&#40;int argc,  char* argv&#91;&#93;&#41;&#123;
    // A char array to hold the game name &#40;arbitrarily sized&#41;
    //
    char * gameName = malloc&#40;400&#41;;

    // Get the title from the eboot
    //
    if&#40;getTitle&#40;"EBOOT.PBP", gameName&#41; > 0&#41;&#123;
        printf&#40;"%s\n", gameName&#41;;
    &#125;else&#123;
        printf&#40;"Error\n"&#41;;
    &#125;

    // Release our title buffer
    //
    free&#40;gameName&#41;;
&#125;
Onii
Posts: 40
Joined: Sun Oct 05, 2008 1:07 pm

Post by Onii »

I couldn't leave well enough alone.

I made a new version that loads the SFO data into memory and processes it that way. It also uses the sceIo calls.

Here are my benchmarking finds (all benchmarks 10000 iterations @ 222mhz from ms0:):

62KB homebrew EBOOT.PBP
Original code with fread and fseek no memory: 74 seconds
Intermediate code with fread fseek and memory*: 35 seconds
Final code with sceIo calls and memory: 21 seconds

75MB ps1 EBOOT.PBP
Original code with fread and fseek no memory: 75 seconds
Intermediate code with fread fseek and memory*: 34 seconds
Final code with sceIo calls and memory: 21 seconds

25MB Official 4.01 firmware update EBOOT.PBP
Original code with fread and fseek no memory: 388 seconds
Intermediate code with fread fseek and memory*: 35 seconds
Final code with sceIo calls and memory: 21 seconds

*I didn't post the intermediate code, it's the same as the final code but using fread and fseek instead of the sceIo equivalent.

I'm pretty happy with the speed at this point. The lag on the firmware test can be attributed to a very large SFO to be parsed, but was included so that we can see that the code works on all sorts of EBOOTS. Notice that the new code treats this as efficiently as a stripped down homebrew EBOOT. I doubt anyone will be doing 10000 loads of any game titles so I'm sure the performance will be up to task.

Anyway here's the final code:

Code: Select all

u32 pspGetTitleFromEBOOT&#40;const char * fileName, char * gameTitle, const char * defaultTitle&#41;&#123;
    u32 SFOOffset = 0;
    u32 SFOEndOffset = 0;
    u32 SFODataLength = 0;

    char * SFOData = NULL;

    u32 i = 0;
    u32 curIndexOffset = 0;

    SceUID fp = sceIoOpen&#40;fileName, PSP_O_RDONLY, 0777&#41;;

    if&#40;!fp&#41;&#123;
        return -1;
    &#125;

    // Skip the EBOOT header
    //
    if&#40;sceIoLseek32&#40;fp, 8, SEEK_SET&#41; == 0&#41;&#123;
        sceIoClose&#40;fp&#41;;
        return -1;
    &#125;

    // Read the offset to the sfo data in the eboot
    //
    if&#40;sceIoRead&#40;fp, &SFOOffset, 4&#41; != 4&#41;&#123;
        sceIoClose&#40;fp&#41;;
        return -1;
    &#125;

    // Read the offset to the end of the sfo data in the eboot
    //
    if&#40;sceIoRead&#40;fp, &SFOEndOffset, 4&#41; != 4&#41;&#123;
        sceIoClose&#40;fp&#41;;
        return -1;
    &#125;

    // Calculate the length of the SFO data
    //
    SFODataLength = SFOEndOffset - SFOOffset;

    // Create a buffer just big enough for the sfo data
    //
    SFOData = malloc&#40;SFODataLength&#41;;

    // If we ran out of memory
    //
    if&#40;!SFOData&#41;&#123;
        sceIoClose&#40;fp&#41;;
        return -1;
    &#125;

    // Skip to the SFO
    //
    if&#40;sceIoLseek32&#40;fp, SFOOffset, SEEK_SET&#41; == 0&#41;&#123;
        sceIoClose&#40;fp&#41;;
        return -1;
    &#125;

    // Read the offset to the end of the sfo data in the eboot
    // return error if we couldn't read it all
    //
    if&#40;sceIoRead&#40;fp, SFOData, SFODataLength&#41; != SFODataLength&#41;&#123;
        sceIoClose&#40;fp&#41;;
        return -1;
    &#125;

    // Close the EBOOT now that we're done with it
    //
    sceIoClose&#40;fp&#41;;

    // Set up a backwards loop to iterate over the key / value index
    // 16 is the offset in the SFO header to get the number of index entries
    //
    for&#40;i = *&#40;&#40;u32 *&#41;&#40;SFOData + 16&#41;&#41; - 1; i >= 0; i--&#41;&#123;
        // Set the current index's offset, normalized tot he buffer start.
        // 20 is the offset just past the SFO header
        //
        curIndexOffset = &#40;16 * i&#41; + 20;

        // First Expression&#58;
        // If the key type is 2 &#40;TXT&#41; this key / value pair might be the title so continue to the expression
        // 3 is the offset within the header for the key type
        //
        // Second Expression&#58;
        // Compare the current key to "TITLE"
        // if it matches, we have the proper key
        // 8 is the offset in the SFO header for the key table offset
        //
        if&#40;*&#40;SFOData + curIndexOffset + 3&#41; == 2 && strcmp&#40;"TITLE", SFOData + &#40;*&#40;&#40;u32 *&#41;&#40;SFOData + 8&#41;&#41;&#41; + &#40;*&#40;&#40;u16 *&#41;&#40;SFOData + curIndexOffset&#41;&#41;&#41;&#41; == 0&#41;&#123;
            // Copy our found string into the char * passed in
            // the first 12 is the offset in the SFO header for the value table offset
            // the second 12 is the offset in the current index for the value offset within the value table
            //
            strcpy&#40;gameTitle, SFOData + &#40;*&#40;&#40;u32 *&#41;&#40;SFOData + 12&#41;&#41;&#41; + &#40;*&#40;&#40;u32 *&#41;&#40;SFOData + curIndexOffset + 12&#41;&#41;&#41;&#41;;

            // Return success
            //
            return 1;
        &#125;
    &#125;

    // If we're here we never found a key that matched TITLE
    // Return the default title
    //
    strcpy&#40;gameTitle, defaultTitle&#41;;

    // Return failure
    //
    return 0;
&#125;
Edit: bug fix, sceIoLseek32() should == 0 not != 0
Last edited by Onii on Thu Oct 09, 2008 10:40 am, edited 1 time in total.
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

Post by Ooblik »

Wow...I have no words... lol
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

It seems like a big deal, but remember this is something you won't do very often, and certainly not 10,000 repetitions in a row. The time difference between the posix functions and the sce functions are negligible when doing this once or twice. Clearly not an area that needs optimizing. :)
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

Post by Ooblik »

After implementing the code the old code works fine but the newer code doesn't return anything. Can I see your main() to make sure I'm using it properly?
Onii
Posts: 40
Joined: Sun Oct 05, 2008 1:07 pm

Post by Onii »

I was thinking about this one on my commute today. I was thinking that it was too fast to be correct. I was right. It was failing out early and my test script failed to detect the error condition.

I updated the code and statistics above. It's still the fastest by a good margin but not an order of magnitude faster, which makes sense.

The bug fix is that both of the seeks need to == 0 not != 0. Flip those two (or copy the updated code above), and let me know if it works.

The new third argument will be returned if no TITLE is found. It can be easily modified to return that string in all the error conditions, by putting the strcpy just before the returns, which is probably what should be done.

Sorry about the bug. Let me know how it goes.

J.F.: Why wouldn't we optimize this? The OP asked for a fast implementation. If we become complacent on the easy things (i.e. it took me about 5 minutes to convert form posix to sce for a ~40% speed increase) what happens when we get to the tough ones?
Ooblik
Posts: 38
Joined: Thu Apr 10, 2008 1:47 pm

Post by Ooblik »

The new function works great! Thanks. If you want in on the beta for the XMB Replacement I'm making I'd be glad to let you test it out.

And to J.F.: thats like telling an artist that he shouldn't mix his paints..
Post Reply