Beispiel #1
0
static upse_psf_t *_upse_load(char *path, int level, int type, upse_iofuncs_t *_upse_iofuncs)	// Type==1 for just info load.
{
    void *fp;
    upse_exe_header_t tmpHead;
    tmpHead.t_addr = 0;
    tmpHead.t_size = 0;
    unsigned char *in, *out = 0;
    u8 head[4];
    u32 reserved;
    u32 complen;
    u32 crc32;
    uLongf outlen;
    upse_psf_t *psfi;
    upse_psf_t *tmpi;

    _ENTER;

    if (!(fp = _upse_iofuncs->open_impl(path, "rb")))
    {
	_ERROR("path %s failed to load\n", path);
	return NULL;
    }

    _upse_iofuncs->read_impl(head, 1, 4, fp);
    if (memcmp(head, "PSF\x01", 4))
	return NULL;

    psfi = calloc(sizeof(upse_psf_t), 1);
    psfi->stop = ~0;
    psfi->fade = 0;

    _upse_iofuncs->read_impl(&reserved, 1, 4, fp);
    _upse_iofuncs->read_impl(&complen, 1, 4, fp);
    complen = BFLIP32(complen);

    _upse_iofuncs->read_impl(&crc32, 1, 4, fp);
    crc32 = BFLIP32(crc32);

    _upse_iofuncs->seek_impl(fp, reserved, SEEK_CUR);

    if (type)
	_upse_iofuncs->seek_impl(fp, complen, SEEK_CUR);
    else
    {
	in = malloc(complen);
	out = malloc(1024 * 1024 * 2 + 0x800);
	_upse_iofuncs->read_impl(in, 1, complen, fp);
	outlen = 1024 * 1024 * 2;
	uncompress(out, &outlen, in, complen);
	free(in);
	memcpy(&tmpHead, out, sizeof(upse_exe_header_t));
	upse_r3000_cpu_regs.pc = BFLIP32(tmpHead.pc0);
	upse_r3000_cpu_regs.GPR.n.gp = BFLIP32(tmpHead.gp0);
	upse_r3000_cpu_regs.GPR.n.sp = BFLIP32(tmpHead.s_addr);
	if (upse_r3000_cpu_regs.GPR.n.sp == 0)
	    upse_r3000_cpu_regs.GPR.n.sp = 0x801fff00;

	if (level)
	{
	    LoadPSXMem(BFLIP32(tmpHead.t_addr), BFLIP32(tmpHead.t_size), out + 0x800);
	    free(out);
	}
    }

    {
	u8 tagdata[5];
	if (_upse_iofuncs->read_impl(tagdata, 1, 5, fp) == 5)
	{
	    if (!memcmp(tagdata, "[TAG]", 5))
	    {
		char linebuf[1024];

		while (upse_io_fgets(linebuf, 1024, fp, _upse_iofuncs))
		{
		    int x;
		    char *key = NULL, *value = NULL;

		    if (!_upse_psf_get_tag(linebuf, &key, &value))
		    {
			if (key)
			    free(key);
			if (value)
			    free(value);
			continue;
		    }

		    _upse_psf_add_tag(&psfi->taglist, key, value);

		    if (!level)
		    {
			static char *yoinks[8] = { "title", "artist", "game", "year", "genre",
			    "copyright", "psfby", "comment"
			};
			char **yoinks2[8] = { &psfi->title, &psfi->artist, &psfi->game, &psfi->year, &psfi->genre,
			    &psfi->copyright, &psfi->psfby, &psfi->comment
			};
			for (x = 0; x < 8; x++)
			    if (!strcasecmp(key, yoinks[x]))
				*yoinks2[x] = value;
			if (!strcasecmp(key, "length"))
			    psfi->stop = _upse_time_to_ms(value);
			else if (!strcasecmp(key, "fade"))
			    psfi->fade = _upse_time_to_ms(value);
		    }

		    if (!strcasecmp(key, "_lib") && !type)
		    {
			char *tmpfn;
			/* Load file name "value" from the directory specified in
			   the full path(directory + file name) "path"
			 */
			tmpfn = _upse_resolve_path(path, value);
			if (!(tmpi = _upse_load(tmpfn, level + 1, 0, _upse_iofuncs)))
			{
			    free(key);
			    free(value);
			    free(tmpfn);
			    if (!level)
				free(out);
			    _upse_iofuncs->close_impl(fp);
			    _upse_psf_free_tags(psfi->taglist);
			    free(psfi);
			    return NULL;
			}
			_upse_psf_free_tags(tmpi->taglist);
			free(tmpi);
			free(tmpfn);
		    }

		    if (key)
		        free(key);
		}
	    }
	}
    }

    _upse_iofuncs->close_impl(fp);

    /* Now, if we're at level 0(main PSF), load the main executable, and any libN stuff */
    if (!level && !type)
    {
	LoadPSXMem(BFLIP32(tmpHead.t_addr), BFLIP32(tmpHead.t_size), out + 0x800);
	free(out);
    }

    if (!type)			/* Load libN */
    {
	upse_libcache_slot_t *cache;
	upse_psftag_t *tag;
	unsigned int libncount = 0;
	unsigned int cur = 0;

	tag = psfi->taglist;
	while (tag)
	{
	    if (!strncasecmp(tag->key, "_lib", 4) && tag->key[4])
		libncount++;
	    tag = tag->next;
	}

	if (libncount)
	{
	    cache = malloc(sizeof(upse_libcache_slot_t) * libncount);

	    tag = psfi->taglist;
	    while (tag)
	    {
		if (!strncasecmp(tag->key, "_lib", 4) && tag->key[4])
		{
		    cache[cur].num = atoi(&tag->key[4]);
		    cache[cur].value = tag->value;
		    cur++;
		}
		tag = tag->next;
	    }
	    qsort(cache, libncount, sizeof(upse_libcache_slot_t), ccomp);
	    for (cur = 0; cur < libncount; cur++)
	    {
		u32 ba[3];
		char *tmpfn;

		if (cache[cur].num < 2)
		    continue;

		ba[0] = upse_r3000_cpu_regs.pc;
		ba[1] = upse_r3000_cpu_regs.GPR.n.gp;
		ba[2] = upse_r3000_cpu_regs.GPR.n.sp;

		/* Load file name "value" from the directory specified in
		   the full path(directory + file name) "path"
		 */
		tmpfn = _upse_resolve_path(path, cache[cur].value);
		if (!(tmpi = _upse_load(tmpfn, level + 1, 0, _upse_iofuncs)))
		{
		    //free(key);
		    //free(value);
		    //free(tmpfn);
		    //_upse_iofuncs->close_impl(fp);
		    //return(0);
		}
		free(tmpfn);
		_upse_psf_free_tags(tmpi->taglist);
		free(tmpi);

		upse_r3000_cpu_regs.pc = ba[0];
		upse_r3000_cpu_regs.GPR.n.gp = ba[1];
		upse_r3000_cpu_regs.GPR.n.sp = ba[2];
	    }
	    free(cache);

	}			// if(libncount)

    }				// if(!type)

    _LEAVE;
    return psfi;
}
Beispiel #2
0
static PSFINFO *LoadPSF(char *path, int level, int type,const char *pathDir) // Type==1 for just info load.
{
    FILE *fp;
    u8 *in,*out=0;
	u8 head[4];
    u32 reserved;
    u32 complen;
    u32 crc32;
    uLongf outlen;
	PSFINFO *psfi=NULL;
	PSFINFO *tmpi=NULL;

    u32 offset, plength;
	
	sexypsf_missing_psflib=0;

#ifdef DEBUG
	fprintf(stderr, "Searching (%s)\n", path);
#endif

	fp=fopen(path,"rb");
    if(!fp) return 0;

	fread(head,1,4,fp);
	if(memcmp(head,"PSF\x01",4)) return(0);

	psfi=malloc(sizeof(PSFINFO));
	if( psfi == NULL ) return(0);
	memset(psfi,0,sizeof(PSFINFO));
    psfi->stop=~0;
    psfi->fade=0;

    fread(&reserved,1,4,fp);
    fread(&complen,1,4,fp);
	complen=BFLIP32(complen);

    fread(&crc32,1,4,fp);
	crc32=BFLIP32(crc32);

#ifdef DEBUG
	fprintf(stderr, "CRC32 = 0x%x\n", crc32);
#endif

    fseek(fp,reserved,SEEK_CUR);

    if(type)
		fseek(fp,complen,SEEK_CUR);
    else {
        in=malloc(complen);
		if( !in ) return(0);
        out=malloc(1024*1024*2+0x800);
		if( !out ) {
			free(in);
			return(0);
		}
        fread(in,1,complen,fp);
        outlen=1024*1024*2;
        uncompress(out,&outlen,in,complen);
        free(in);

		psxRegs->pc = out[0x10] | out[0x11]<<8 | out[0x12]<<16 | out[0x13]<<24;
		psxRegs->GPR.n.gp = out[0x14] | out[0x15]<<8 | out[0x16]<<16 | out[0x17]<<24;
		psxRegs->GPR.n.sp = out[0x30] | out[0x31]<<8 | out[0x32]<<16 | out[0x33]<<24;

        if (psxRegs->GPR.n.sp == 0) psxRegs->GPR.n.sp = 0x801fff00;

#ifdef DEBUG
		fprintf(stderr, "%d Level: PC %x GP %x SP %x\n",
			level, psxRegs->pc, psxRegs->GPR.n.gp, psxRegs->GPR.n.sp);
#endif

 		if(level) {
			offset = out[0x18] | out[0x19]<<8 | out[0x1a]<<16 | out[0x1b]<<24;
			offset &= 0x3fffffff;	// kill any MIPS cache segment indicators
			plength = out[0x1c] | out[0x1d]<<8 | out[0x1e]<<16 | out[0x1f]<<24;

#ifdef DEBUG
			fprintf(stderr, "%d Level: offset %x plength: %d [%d]\n", level, offset, plength, plength);
#endif

			LoadPSXMem(offset,plength,(char*)out+0x800);
			free(out);

			// I don't really know how does this
			// work, but apparently it breaks the
			// loop that makes ff6 and ct to take
			// several seconds to start ...
			if( crc32 == 0xEFDE8EEE || crc32 == 0x545D9F65 )
				psxMemWrite32(0x5A66C, 0);

			// Same as above for Popolocrois
			if( crc32 == 0xF535726 )
				psxMemWrite32(0x5A990, 0);
		}
	}

    {
		u8 tagdata[5];
        if(fread(tagdata,1,5,fp)==5) {
			if(!memcmp(tagdata,"[TAG]",5)) {
				char linebuf[1024];
				while(fgets(linebuf,1024,fp)>0) {
					int x;
					char *key=0,*value=0;

					if(!GetKeyVal(linebuf,&key,&value)) {
						if(key) free(key);
						if(value) free(value);
						continue;
					}

					AddKV(&psfi->tags,key,value);

					if(!level) {
       					static char *yoinks[8]={"title","artist","game","year","genre",
                                  "copyright","psfby","comment"};
						char **yoinks2[8]={&psfi->title,&psfi->artist,&psfi->game,&psfi->year,&psfi->genre,
                                    &psfi->copyright,&psfi->psfby,&psfi->comment};
						for(x=0;x<8;x++)
							if(!strcasecmp(key,yoinks[x]))
								*yoinks2[x]=value;
						if(!strcasecmp(key,"length"))
							psfi->stop=TimeToMS(value);
						else if(!strcasecmp(key,"fade"))
							psfi->fade=TimeToMS(value);
					}

					if(!strcasecmp(key,"_lib") && !type) {
						char *tmpfn;
						/* Load file name "value" from the directory specified in
						   the full path(directory + file name) "path"
						*/
                        //printf("yo %s\nya %s\nyu%s\n",path,value,pathDir);
						tmpfn=GetFileWithBase(path,value,pathDir);
                        //printf("ru: %s\n",tmpfn);
						if(!(tmpi=LoadPSF(tmpfn,level+1,0,pathDir))) {
							//free(key);
							//free(value);
							sexypsf_missing_psflib=1;
							strcpy(sexypsf_psflib_str,value);
							free(tmpfn);
 							if(!level) free(out);
							fclose(fp);
							FreeTags(psfi->tags);
							free(psfi);
							return(0);
						}
						FreeTags(tmpi->tags);
						free(tmpi);
						free(tmpfn);
					}
				}
			}
		}
	}

    fclose(fp);

	/* Now, if we're at level 0(main PSF), load the main executable, and any libN stuff */
    if(!level && !type) {
		offset = out[0x18] | out[0x19]<<8 | out[0x1a]<<16 | out[0x1b]<<24;
		offset &= 0x3fffffff;	// kill any MIPS cache segment indicators
		plength = out[0x1c] | out[0x1d]<<8 | out[0x1e]<<16 | out[0x1f]<<24;

		// TODO - investigate: should i just make
		//        plength = outlen-0x800?

		// Philosoma has an illegal "plength".  *sigh*
		if (plength > (outlen-0x800))
		{
			plength = outlen-0x800;
		}

		if( psfi->game ) {
			// Suikoden Tenmei has an illegal "plength". *sigh sigh*
			if( !strncmp(psfi->game, "Suikoden: Tenmei no Chikai", 26) ) {
				plength = outlen-0x800;
			}

			// Einhänder has an illegal "plength". *sigh sigh sigh*
			if( !strncmp(psfi->game, "Einhänder", 9) ) {
				plength = outlen-0x800;
			}
		}

#ifdef DEBUG
		fprintf(stderr, "%d Level: offset %x plength: %d [%d]\n", level, offset, plength, outlen-2048);
#endif

		LoadPSXMem(offset,plength,(char*)out+0x800);
		free(out);
    }

	if(!type)	/* Load libN */{
		LIBNCACHE *cache;
		PSFTAG *tag;
		unsigned int libncount=0;
		unsigned int cur=0;

		tag=psfi->tags;
		while(tag) {
			if(!strncasecmp(tag->key,"_lib",4) && tag->key[4])
			libncount++;
			tag=tag->next;
		}

		if(libncount) {
			cache=malloc(sizeof(LIBNCACHE)*libncount);

			tag=psfi->tags;
			while(tag) {
				if(!strncasecmp(tag->key,"_lib",4) && tag->key[4]) {
					cache[cur].num=atoi(&tag->key[4]);
					cache[cur].value=tag->value;
					cur++;
				}
				tag=tag->next;
			}
			qsort(cache, libncount, sizeof(LIBNCACHE), ccomp);
			for(cur=0;cur<libncount;cur++) {
				u32 ba[3];
				char *tmpfn;

 				if(cache[cur].num < 2) continue;

				ba[0]=psxRegs->pc;
				ba[1]=psxRegs->GPR.n.gp;
				ba[2]=psxRegs->GPR.n.sp;

				/* Load file name "value" from the directory specified in
				the full path(directory + file name) "path"
				*/
				tmpfn=GetFileWithBase(path,cache[cur].value,pathDir);
				if(!(tmpi=LoadPSF(tmpfn,level+1,0,pathDir))) {
					//free(key);
					//free(value);
					//free(tmpfn);
					//fclose(fp);
			        //return(0);
				} else {
                    FreeTags(tmpi->tags);
                    free(tmpi);
                }
                free(tmpfn);


				psxRegs->pc=ba[0];
       			psxRegs->GPR.n.gp=ba[1];
				psxRegs->GPR.n.sp=ba[2];
			}
			free(cache);
		}	// if(libncount)

	}	// if(!type)

	return(psfi);
}
static PSFINFO *LoadPSF(char *path, int level, int type) // Type==1 for just info load.
{
        FILE *fp;
        EXE_HEADER tmpHead;
        char *in,*out=0;
	u8 head[4];
        u32 reserved;
        u32 complen;
        u32 crc32; 
        uLongf outlen;
	PSFINFO *psfi;
	PSFINFO *tmpi;

	//printf("Loading: %s\n",path);
        if(!(fp=fopen(path,"rb")))
 	{
	 return(0);
	}
 
	fread(head,1,4,fp);
	if(memcmp(head,"PSF\x01",4)) return(0);

	psfi=malloc(sizeof(PSFINFO));
	memset(psfi,0,sizeof(PSFINFO));
        psfi->stop=~0;
        psfi->fade=0; 

        fread(&reserved,1,4,fp);
        fread(&complen,1,4,fp);
	complen=BFLIP32(complen);

        fread(&crc32,1,4,fp);
	crc32=BFLIP32(crc32);

        fseek(fp,reserved,SEEK_CUR);

        if(type)
	 fseek(fp,complen,SEEK_CUR);
        else
        {
         in=malloc(complen);
         out=malloc(1024*1024*2+0x800);
         fread(in,1,complen,fp);
         outlen=1024*1024*2;
         uncompress(out,&outlen,in,complen);
         free(in);
         memcpy(&tmpHead,out,sizeof(EXE_HEADER));
         psxRegs.pc = BFLIP32(tmpHead.pc0);
         psxRegs.GPR.n.gp = BFLIP32(tmpHead.gp0);
         psxRegs.GPR.n.sp = BFLIP32(tmpHead.S_addr);
         if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;

 	 if(level)
 	 {
	  LoadPSXMem(BFLIP32(tmpHead.t_addr),BFLIP32(tmpHead.t_size),out+0x800);
          free(out);
	 }
        }

        {
         u8 tagdata[5];
         if(fread(tagdata,1,5,fp)==5)
         {
          if(!memcmp(tagdata,"[TAG]",5))
          {
           char linebuf[1024];

           while(fgets(linebuf,1024,fp)>0)
           {
            int x;
	    char *key=0,*value=0;

	    if(!GetKeyVal(linebuf,&key,&value))
	    { 
	     if(key) free(key);
	     if(value) free(value);
	     continue;
            }

	    AddKV(&psfi->tags,key,value);

	    if(!level)
	    {
       	     static char *yoinks[8]={"title","artist","game","year","genre",
                                  "copyright","psfby","comment"};
	     char **yoinks2[8]={&psfi->title,&psfi->artist,&psfi->game,&psfi->year,&psfi->genre,
                                    &psfi->copyright,&psfi->psfby,&psfi->comment};
	     for(x=0;x<8;x++)
	      if(!strcasecmp(key,yoinks[x]))
		*yoinks2[x]=value;
	     if(!strcasecmp(key,"length"))
	      psfi->stop=TimeToMS(value);
	     else if(!strcasecmp(key,"fade"))
	      psfi->fade=TimeToMS(value);
	    }

	    if(!strcasecmp(key,"_lib") && !type)
	    {
	     char *tmpfn;
	     /* Load file name "value" from the directory specified in
		the full path(directory + file name) "path"
	     */
	     tmpfn=findFileIgnoreCase(path,value);
	     if(!(tmpi=LoadPSF(tmpfn,level+1,0)))
	     {
	      //free(key);
	      //free(value); // key and value will be freed by FreeTags(psfi->tags)
	      free(tmpfn);
 	      if(!level) free(out);
	      fclose(fp);
	      FreeTags(psfi->tags);
	      free(psfi);
	      return(0);
	     }
	     FreeTags(tmpi->tags);
	     free(tmpi);
	     free(tmpfn);
	    }
           }
          }
         }
        }  

        fclose(fp);

	/* Now, if we're at level 0(main PSF), load the main executable, and any libN stuff */
        if(!level && !type)
        {
         LoadPSXMem(BFLIP32(tmpHead.t_addr),BFLIP32(tmpHead.t_size),out+0x800);
	 free(out);
        }

	if(!type)	/* Load libN */
	{
	 LIBNCACHE *cache;
	 PSFTAG *tag;
	 unsigned int libncount=0;
	 unsigned int cur=0;

	 tag=psfi->tags;
	 while(tag)
	 {
	  if(!strncasecmp(tag->key,"_lib",4) && tag->key[4])
	   libncount++;
	  tag=tag->next;
	 }

	 if(libncount)
	 {
	  cache=malloc(sizeof(LIBNCACHE)*libncount);

	  tag=psfi->tags;
	  while(tag)
	  {
	   if(!strncasecmp(tag->key,"_lib",4) && tag->key[4])
	   {
	    cache[cur].num=atoi(&tag->key[4]);
	    cache[cur].value=tag->value;
	    cur++;
	   }
	   tag=tag->next;
	  }
	  qsort(cache, libncount, sizeof(LIBNCACHE), ccomp);
	  for(cur=0;cur<libncount;cur++)
	  {
	   u32 ba[3];
	   char *tmpfn;

 	   if(cache[cur].num < 2) continue;

	   ba[0]=psxRegs.pc;
	   ba[1]=psxRegs.GPR.n.gp;
	   ba[2]=psxRegs.GPR.n.sp;

           /* Load file name "value" from the directory specified in
                the full path(directory + file name) "path"
             */
           tmpfn=findFileIgnoreCase(path,cache[cur].value);
           if(!(tmpi=LoadPSF(tmpfn,level+1,0)))
           {
            //free(key);
            //free(value);
            //free(tmpfn);
            //fclose(fp);
            //return(0);
           }
           free(tmpfn);
	   FreeTags(tmpi->tags);
	   free(tmpi);

	   psxRegs.pc=ba[0];
       	   psxRegs.GPR.n.gp=ba[1];
	   psxRegs.GPR.n.sp=ba[2];
	  }
	  free(cache);

	 }	// if(libncount)

	}	// if(!type)

	return(psfi);
}
Beispiel #4
0
// LoadPSF for Memory
static PSFINFO *memLoadPSF(char *addr, long size, int level, int type) // Type==1 for just info load.
{
    u8 *in,*out=0;
	u8 head[4];

    u32 reserved;
    u32 complen;
    u32 crc32;
    uLongf outlen;
	PSFINFO *psfi=NULL;
	PSFINFO *tmpi=NULL;
	u32 poffset, plength;
	u32 offset = 0;

	if( addr == NULL || size == 0 ) return(0);

	memread((void*)(head), 4, addr, size, &offset);
	if(memcmp(head,"PSF\x01",4)) return(0);

	psfi=malloc(sizeof(PSFINFO));
	memset(psfi,0,sizeof(PSFINFO));
    psfi->stop=~0;
    psfi->fade=0;

	memread((void*)(&reserved), 4, addr, size, &offset);
	memread((void*)(&complen), 4, addr, size, &offset);
	complen=BFLIP32(complen);

	memread((void*)(&crc32), 4, addr, size, &offset);
	crc32=BFLIP32(crc32);

	memseek(reserved, size, &offset);

	if(type) {
		memseek(complen, size, &offset);
	}
    else {
        in=malloc(complen);
        out=malloc(1024*1024*2+0x800);
		if( !in || !out ) { free(psfi); return (0); }
		memread((void*)(in), complen, addr, size, &offset);

        outlen=1024*1024*2;
        uncompress(out,&outlen,in,complen);
        free(in);

        psxRegs->pc = out[0x10] | out[0x11]<<8 | out[0x12]<<16 | out[0x13]<<24;
		psxRegs->GPR.n.gp = out[0x14] | out[0x15]<<8 | out[0x16]<<16 | out[0x17]<<24;
		psxRegs->GPR.n.sp = out[0x30] | out[0x31]<<8 | out[0x32]<<16 | out[0x33]<<24;

        if (psxRegs->GPR.n.sp == 0) psxRegs->GPR.n.sp = 0x801fff00;

#ifdef DEBUG
		fprintf(stderr, "%d Level: PC %x GP %x SP %x\n",
			level, psxRegs->pc, psxRegs->GPR.n.gp, psxRegs->GPR.n.sp);
#endif

 		if(level) {
			poffset = out[0x18] | out[0x19]<<8 | out[0x1a]<<16 | out[0x1b]<<24;
			poffset &= 0x3fffffff;	// kill any MIPS cache segment indicators
			plength = out[0x1c] | out[0x1d]<<8 | out[0x1e]<<16 | out[0x1f]<<24;

#ifdef DEBUG
			fprintf(stderr, "%d Level: offset %x plength: %d\n", level, poffset, plength);
#endif

			LoadPSXMem(poffset,plength,out+0x800);
			free(out);

			// I don't really know how does this
			// work, but apparently it breaks the
			// loop that makes ff6 and ct to take
			// several seconds to start ...
			if( crc32 == 0xEFDE8EEE || crc32 == 0x545D9F65 )
				psxMemWrite32(0x5A66C, 0);

			// Same as above for Popolocrois and Einhänder
			if( crc32 == 0xF535726 )
				psxMemWrite32(0x5A990, 0);
		}
	}

    {
		u8 tagdata[5];
		if( memread((void*)(tagdata), 5, addr, size, &offset) == 5 ) {
			if(!memcmp(tagdata,"[TAG]",5)) {
				char linebuf[1024];

				while( memfgets(linebuf, 1024, addr, size, &offset)>0 ) {
					int x;
					char *key=0,*value=0;

					if(!GetKeyVal(linebuf,&key,&value)) {
						if(key) free(key);
						if(value) free(value);
						continue;
					}

					AddKV(&psfi->tags,key,value);

					if(!level) {
       					static char *yoinks[8]={"title","artist","game","year","genre",
                                  "copyright","psfby","comment"};
						char **yoinks2[8]={&psfi->title,&psfi->artist,&psfi->game,&psfi->year,&psfi->genre,
                                    &psfi->copyright,&psfi->psfby,&psfi->comment};
						for(x=0;x<8;x++)
							if(!strcasecmp(key,yoinks[x]))
								*yoinks2[x]=value;
						if(!strcasecmp(key,"length"))
							psfi->stop=TimeToMS(value);
						else if(!strcasecmp(key,"fade"))
							psfi->fade=TimeToMS(value);
					}

					if(!strcasecmp(key,"_lib") && !type && __psflibs) {

						/* Search file name "value" from the __psflibs array */
						int n, found = 0;
						for(n=0;n<__psflibs->count;n++)
							if( !strcasecmp(value, __psflibs->libn[n].name) ) {
								found = 1;
								break;
							}

						if(!found || !(tmpi=memLoadPSF(__psflibs->libn[n].addr, __psflibs->libn[n].size, level+1,0))) {
							free(key);
							free(value);
							if(!level) free(out);

							FreeTags(psfi->tags);
							free(psfi);
							return(0);
						}

						FreeTags(tmpi->tags);
						free(tmpi);
					}
				}
			}
		}
	}

	/* Now, if we're at level 0(main PSF), load the main executable, and any libN stuff*/
    if(!level && !type) {
    	poffset = out[0x18] | out[0x19]<<8 | out[0x1a]<<16 | out[0x1b]<<24;
		poffset &= 0x3fffffff;	// kill any MIPS cache segment indicators
		plength = out[0x1c] | out[0x1d]<<8 | out[0x1e]<<16 | out[0x1f]<<24;

		// TODO - investigate: should i just make
		//        plength = outlen-0x800?

		// Philosoma has an illegal "plength".  *sigh*
		if (plength > (outlen-0x800))
		{
			plength = outlen-0x800;
		}

		if( psfi->game ) {
			// Suikoden Tenmei has an illegal "plength". *sigh sigh*
			if( !strncmp(psfi->game, "Suikoden: Tenmei no Chikai", 26) ) {
				plength = outlen-0x800;
			}

			// Einhänder has an illegal "plength". *sigh sigh sigh*
			if( !strncmp(psfi->game, "Einhänder", 9) ) {
				plength = outlen-0x800;
			}
		}

#ifdef DEBUG
		fprintf(stderr, "%d Level: offset %x plength: %d\n", level, poffset, plength);
#endif

		LoadPSXMem(poffset,plength,out+0x800);
		free(out);
    }

	if(!type && __psflibs)	/* Load libN */ {
		LIBNCACHE *cache;
		PSFTAG *tag;
		unsigned int libncount=0;
		unsigned int cur=0;

		tag=psfi->tags;
		while(tag) {
			if(!strncasecmp(tag->key,"_lib",4) && tag->key[4])
			libncount++;
			tag=tag->next;
		}

		if(libncount) {
			cache=malloc(sizeof(LIBNCACHE)*libncount);

			tag=psfi->tags;
			while(tag) {
				if(!strncasecmp(tag->key,"_lib",4) && tag->key[4]) {
					cache[cur].num=atoi(&tag->key[4]);
					cache[cur].value=tag->value;
					cur++;
				}
				tag=tag->next;
			}
			qsort(cache, libncount, sizeof(LIBNCACHE), ccomp);
			for(cur=0;cur<libncount;cur++) {
				u32 ba[3];

 				if(cache[cur].num < 2) continue;

				ba[0]=psxRegs->pc;
				ba[1]=psxRegs->GPR.n.gp;
				ba[2]=psxRegs->GPR.n.sp;

				/* Search file name "value" from the __psflibs array */
				int n, found = 0;
				for(n=0;n<__psflibs->count;n++)
					if( !strcasecmp(cache[cur].value, __psflibs->libn[n].name) ) {
						found = 1;
						break;
					}

				if(!found || !(tmpi=memLoadPSF(__psflibs->libn[n].addr,__psflibs->libn[n].size,level+1,0))) {
					//free(key);
					//free(value);
					//free(tmpfn);
					//fclose(fp);
					//return(0);
				}
				FreeTags(tmpi->tags);
				free(tmpi);

				psxRegs->pc=ba[0];
       			psxRegs->GPR.n.gp=ba[1];
				psxRegs->GPR.n.sp=ba[2];
			}
			free(cache);
		}	// if(libncount)

	}	// if(!type)

	return(psfi);
}