2008-12-11 18:38:29 +01:00
|
|
|
/*
|
|
|
|
* file_dvd.c
|
|
|
|
*
|
2009-05-13 16:26:55 +02:00
|
|
|
* ISO9660/Joliet DVD loading support
|
2008-12-11 18:38:29 +01:00
|
|
|
*
|
2009-05-13 16:26:55 +02:00
|
|
|
* Softdev (2006)
|
|
|
|
* Eke-Eke (2007,2008,2009)
|
2008-12-11 18:38:29 +01:00
|
|
|
*
|
2009-05-13 16:26:55 +02:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
2008-12-11 18:38:29 +01:00
|
|
|
*
|
2009-05-13 16:26:55 +02:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2008-12-10 19:16:30 +01:00
|
|
|
*
|
2009-05-13 16:26:55 +02:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2008-12-10 19:16:30 +01:00
|
|
|
*
|
2008-12-11 18:38:29 +01:00
|
|
|
********************************************************************************/
|
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
#include "shared.h"
|
2009-05-19 18:14:43 +02:00
|
|
|
#include "gui.h"
|
2008-12-10 19:16:30 +01:00
|
|
|
#include "dvd.h"
|
2008-12-11 18:38:29 +01:00
|
|
|
#include "unzip.h"
|
|
|
|
#include "filesel.h"
|
2009-05-25 19:07:34 +02:00
|
|
|
#include "file_fat.h"
|
|
|
|
#include "file_dvd.h"
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
/** Minimal ISO Directory Definition **/
|
|
|
|
#define RECLEN 0 /* Record length */
|
|
|
|
#define EXTENT 6 /* Extent */
|
|
|
|
#define FILE_LENGTH 14 /* File length (BIG ENDIAN) */
|
|
|
|
#define FILE_FLAGS 25 /* File flags */
|
|
|
|
#define FILENAME_LENGTH 32 /* Filename length */
|
|
|
|
#define FILENAME 33 /* ASCIIZ filename */
|
|
|
|
|
|
|
|
/** Minimal Primary Volume Descriptor **/
|
|
|
|
#define PVDROOT 0x9c
|
|
|
|
|
|
|
|
/** Static Variables **/
|
2009-05-26 15:14:33 +02:00
|
|
|
static u64 rootdir = 0;
|
|
|
|
static u64 basedir = 0;
|
|
|
|
static int rootdirlength = 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
static int IsJoliet = 0;
|
|
|
|
static int diroffset = 0;
|
2009-05-26 15:14:33 +02:00
|
|
|
static int haveDVDdir = 0;
|
2009-10-16 13:32:55 +02:00
|
|
|
static char dvdbuffer[DVDCHUNK];
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Primary Volume Descriptor
|
|
|
|
*
|
|
|
|
* The PVD should reside between sector 16 and 31.
|
|
|
|
* This is for single session DVD only.
|
|
|
|
****************************************************************************/
|
2009-05-19 18:14:43 +02:00
|
|
|
static int getpvd()
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
int sector = 16;
|
|
|
|
u32 rootdir32;
|
|
|
|
|
|
|
|
basedir = rootdirlength = 0;
|
|
|
|
IsJoliet = -1;
|
|
|
|
|
|
|
|
/** Look for Joliet PVD first **/
|
|
|
|
while (sector < 32)
|
|
|
|
{
|
2009-10-16 13:32:55 +02:00
|
|
|
if (dvd_read (&dvdbuffer, DVDCHUNK, (u64)(sector << 11)))
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
if (memcmp (&dvdbuffer, "\2CD001\1", 8) == 0)
|
|
|
|
{
|
|
|
|
memcpy(&rootdir32, &dvdbuffer[PVDROOT + EXTENT], 4);
|
|
|
|
basedir = (u64)rootdir32;
|
|
|
|
memcpy (&rootdirlength, &dvdbuffer[PVDROOT + FILE_LENGTH], 4);
|
|
|
|
basedir <<= 11;
|
|
|
|
IsJoliet = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-05-07 20:25:27 +02:00
|
|
|
else
|
|
|
|
return 0; /*** Can't read sector! ***/
|
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
sector++;
|
|
|
|
}
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
if (IsJoliet > 0)
|
|
|
|
return 1; /*** Joliet PVD Found ? ***/
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
/*** Look for standard ISO9660 PVD ***/
|
|
|
|
sector = 16;
|
|
|
|
while (sector < 32)
|
|
|
|
{
|
2009-10-16 13:32:55 +02:00
|
|
|
if (dvd_read (&dvdbuffer, DVDCHUNK, (u64)(sector << 11)))
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
if (memcmp (&dvdbuffer, "\1CD001\1", 8) == 0)
|
|
|
|
{
|
|
|
|
memcpy (&rootdir32, &dvdbuffer[PVDROOT + EXTENT], 4);
|
|
|
|
basedir = (u64)rootdir32;
|
|
|
|
memcpy (&rootdirlength, &dvdbuffer[PVDROOT + FILE_LENGTH], 4);
|
|
|
|
IsJoliet = 0;
|
|
|
|
basedir <<= 11;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-05-07 20:25:27 +02:00
|
|
|
else
|
|
|
|
return 0; /*** Can't read sector! ***/
|
|
|
|
|
|
|
|
sector++;
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return (IsJoliet == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* getentry
|
|
|
|
*
|
|
|
|
* Support function to return the next file entry, if any
|
|
|
|
* Declared static to avoid accidental external entry.
|
|
|
|
****************************************************************************/
|
2009-05-19 18:14:43 +02:00
|
|
|
static int getentry(int entrycount)
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
char fname[512]; /* Huge, but experience has determined this */
|
|
|
|
char *ptr;
|
|
|
|
char *filename;
|
|
|
|
char *filenamelength;
|
|
|
|
char *rr;
|
|
|
|
int j;
|
|
|
|
u32 offset32;
|
|
|
|
|
|
|
|
/* Basic checks */
|
2010-05-07 20:25:27 +02:00
|
|
|
if (entrycount >= MAXFILES)
|
|
|
|
return 0;
|
|
|
|
if (diroffset >= DVDCHUNK)
|
|
|
|
return 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
/** Decode this entry **/
|
|
|
|
if (dvdbuffer[diroffset]) /* Record length available */
|
|
|
|
{
|
|
|
|
/* Update offsets into sector buffer */
|
|
|
|
ptr = (char *) &dvdbuffer[0];
|
|
|
|
ptr += diroffset;
|
|
|
|
filename = ptr + FILENAME;
|
|
|
|
filenamelength = ptr + FILENAME_LENGTH;
|
|
|
|
|
|
|
|
/* Check for wrap round - illegal in ISO spec,
|
|
|
|
* but certain crap writers do it! */
|
2010-05-07 20:25:27 +02:00
|
|
|
if ((diroffset + dvdbuffer[diroffset]) > DVDCHUNK)
|
|
|
|
return 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
if (*filenamelength)
|
|
|
|
{
|
|
|
|
memset (&fname, 0, 512);
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
/** Do ISO 9660 first **/
|
|
|
|
if (!IsJoliet)
|
|
|
|
strcpy (fname, filename);
|
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
else
|
|
|
|
{
|
2010-05-07 20:25:27 +02:00
|
|
|
/** The more tortuous unicode joliet entries **/
|
2008-12-10 19:16:30 +01:00
|
|
|
for (j = 0; j < (*filenamelength >> 1); j++)
|
|
|
|
fname[j] = filename[j * 2 + 1];
|
|
|
|
fname[j] = 0;
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
if (strlen (fname) >= MAXJOLIET)
|
|
|
|
fname[MAXJOLIET - 1] = 0;
|
|
|
|
if (strlen (fname) == 0)
|
|
|
|
fname[0] = filename[0];
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
if (strlen (fname) == 0)
|
|
|
|
strcpy (fname, ".");
|
2008-12-10 19:16:30 +01:00
|
|
|
else
|
|
|
|
{
|
2010-05-07 20:25:27 +02:00
|
|
|
if (fname[0] == 1)
|
|
|
|
strcpy (fname, "..");
|
2008-12-10 19:16:30 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Move *filenamelength to t,
|
|
|
|
* Only to stop gcc warning for noobs :)
|
|
|
|
*/
|
|
|
|
int t = *filenamelength;
|
|
|
|
fname[t] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
/* Rockridge Check */
|
2008-12-10 19:16:30 +01:00
|
|
|
rr = strstr (fname, ";");
|
2010-05-07 20:25:27 +02:00
|
|
|
if (rr != NULL)
|
|
|
|
*rr = 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
strcpy (filelist[entrycount].filename, fname);
|
|
|
|
memcpy (&offset32, &dvdbuffer[diroffset + EXTENT], 4);
|
|
|
|
filelist[entrycount].offset = (u64)offset32;
|
|
|
|
memcpy (&filelist[entrycount].length, &dvdbuffer[diroffset + FILE_LENGTH], 4);
|
|
|
|
memcpy (&filelist[entrycount].flags, &dvdbuffer[diroffset + FILE_FLAGS], 1);
|
|
|
|
filelist[entrycount].offset <<= 11;
|
|
|
|
filelist[entrycount].flags = filelist[entrycount].flags & 2;
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
/* Prepare for next entry */
|
2008-12-10 19:16:30 +01:00
|
|
|
diroffset += dvdbuffer[diroffset];
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
2009-05-26 15:14:33 +02:00
|
|
|
* DVD_ClearDirectory
|
|
|
|
*
|
|
|
|
* Clear DVD directory flag
|
|
|
|
***************************************************************************/
|
|
|
|
void DVD_ClearDirectory(void)
|
|
|
|
{
|
|
|
|
haveDVDdir = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* DVD_UpdateDirectory
|
2008-12-10 19:16:30 +01:00
|
|
|
*
|
|
|
|
* Update DVD current root directory
|
|
|
|
***************************************************************************/
|
2009-05-26 15:14:33 +02:00
|
|
|
int DVD_UpdateDirectory(bool go_up, u64 offset, u32 length)
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
/* root has no parent directory */
|
2010-05-18 09:35:29 +02:00
|
|
|
if ((basedir == rootdir) && (go_up || (offset == basedir)))
|
2010-05-07 20:25:27 +02:00
|
|
|
return 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
2009-05-25 19:07:34 +02:00
|
|
|
/* simply update current root directory */
|
|
|
|
rootdir = offset;
|
|
|
|
rootdirlength = length;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* DVD_ParseDirectory
|
|
|
|
*
|
|
|
|
* This function will parse the directory tree.
|
|
|
|
* It relies on rootdir and rootdirlength being pre-populated by a call to
|
|
|
|
* getpvd, a previous parse or a menu selection.
|
|
|
|
*
|
|
|
|
* The return value is number of files collected, or 0 on failure.
|
|
|
|
****************************************************************************/
|
2009-05-19 18:14:43 +02:00
|
|
|
int DVD_ParseDirectory(void)
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
int pdlength;
|
|
|
|
u64 pdoffset;
|
|
|
|
u64 rdoffset;
|
|
|
|
int len = 0;
|
|
|
|
int filecount = 0;
|
|
|
|
|
|
|
|
pdoffset = rdoffset = rootdir;
|
|
|
|
pdlength = rootdirlength;
|
|
|
|
filecount = 0;
|
|
|
|
|
|
|
|
/** Clear any existing values ***/
|
|
|
|
memset (&filelist, 0, sizeof (FILEENTRIES) * MAXFILES);
|
|
|
|
|
|
|
|
/*** Get as many files as possible ***/
|
|
|
|
while (len < pdlength)
|
|
|
|
{
|
2010-05-07 20:25:27 +02:00
|
|
|
if (dvd_read (&dvdbuffer, DVDCHUNK, pdoffset) == 0)
|
|
|
|
return 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
diroffset = 0;
|
|
|
|
|
2009-05-06 16:47:22 +02:00
|
|
|
while (getentry (filecount))
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
2009-05-07 15:00:57 +02:00
|
|
|
if (strcmp(filelist[filecount].filename,".") && (filecount < MAXFILES))
|
2009-05-06 16:47:22 +02:00
|
|
|
filecount++;
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
|
2009-10-16 13:32:55 +02:00
|
|
|
len += DVDCHUNK;
|
2008-12-10 19:16:30 +01:00
|
|
|
pdoffset = rdoffset + len;
|
|
|
|
}
|
2008-12-14 13:38:52 +01:00
|
|
|
|
|
|
|
/* Sort the file list */
|
|
|
|
qsort(filelist, filecount, sizeof(FILEENTRIES), FileSortCallback);
|
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
return filecount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* DVD_LoadFile
|
|
|
|
*
|
|
|
|
* This function will load a BIN, SMD or ZIP file from DVD into the ROM buffer.
|
|
|
|
* The index values indicates the file position in filentry list
|
|
|
|
* This functions return the actual size of data copied into the buffer
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2009-05-25 19:07:34 +02:00
|
|
|
int DVD_LoadFile(u8 *buffer, u32 selection)
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
/* file size */
|
|
|
|
int length = filelist[selection].length;
|
|
|
|
|
|
|
|
if (length > 0)
|
|
|
|
{
|
|
|
|
/* Read first data chunk */
|
2009-10-16 13:32:55 +02:00
|
|
|
char readbuffer[DVDCHUNK];
|
2008-12-10 19:16:30 +01:00
|
|
|
u64 discoffset = filelist[selection].offset;
|
2009-10-16 13:32:55 +02:00
|
|
|
dvd_read (&readbuffer, DVDCHUNK, discoffset);
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
/* determine file type */
|
|
|
|
if (!IsZipFile ((char *) readbuffer))
|
|
|
|
{
|
2010-05-28 14:08:00 +02:00
|
|
|
if (length > MAXROMSIZE)
|
|
|
|
{
|
|
|
|
GUI_WaitPrompt("Error","File size not supported !");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char msg[64];
|
2008-12-11 18:38:29 +01:00
|
|
|
sprintf(msg,"Loading %d bytes...", length);
|
2009-05-23 19:29:53 +02:00
|
|
|
GUI_MsgBoxOpen("Information",msg,1);
|
2010-05-28 14:08:00 +02:00
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
/* How many 2k blocks to read */
|
2009-10-16 13:32:55 +02:00
|
|
|
int blocks = length / DVDCHUNK;
|
2008-12-10 19:16:30 +01:00
|
|
|
int readoffset = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* read data chunks */
|
|
|
|
for (i = 0; i < blocks; i++)
|
|
|
|
{
|
2009-10-16 13:32:55 +02:00
|
|
|
dvd_read(readbuffer, DVDCHUNK, discoffset);
|
|
|
|
discoffset += DVDCHUNK;
|
|
|
|
memcpy (buffer + readoffset, readbuffer, DVDCHUNK);
|
|
|
|
readoffset += DVDCHUNK;
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* final read */
|
2009-10-16 13:32:55 +02:00
|
|
|
i = length % DVDCHUNK;
|
2008-12-10 19:16:30 +01:00
|
|
|
if (i)
|
|
|
|
{
|
2009-10-16 13:32:55 +02:00
|
|
|
dvd_read (readbuffer, DVDCHUNK, discoffset);
|
2008-12-10 19:16:30 +01:00
|
|
|
memcpy (buffer + readoffset, readbuffer, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-12-11 18:38:29 +01:00
|
|
|
return UnZipBuffer (buffer, discoffset, NULL);
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* DVD_Open
|
|
|
|
*
|
|
|
|
* Function to load a DVD directory and display to user.
|
|
|
|
****************************************************************************/
|
|
|
|
|
2009-05-19 18:14:43 +02:00
|
|
|
int DVD_Open(void)
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
|
|
|
/* is DVD mounted ? */
|
|
|
|
if (!getpvd())
|
|
|
|
{
|
2010-05-07 20:25:27 +02:00
|
|
|
/* remount DVD */
|
2009-05-23 19:29:53 +02:00
|
|
|
GUI_MsgBoxOpen("Information", "Mounting DVD ...",1);
|
2010-05-07 20:25:27 +02:00
|
|
|
haveDVDdir = 0;
|
2008-12-10 19:16:30 +01:00
|
|
|
|
|
|
|
#ifdef HW_RVL
|
|
|
|
u32 val;
|
|
|
|
DI_GetCoverRegister(&val);
|
|
|
|
|
|
|
|
if(val & 0x1)
|
|
|
|
{
|
2009-05-20 17:11:06 +02:00
|
|
|
GUI_WaitPrompt("Error","No Disc inserted !");
|
2008-12-10 19:16:30 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DI_Mount();
|
2009-05-23 19:29:53 +02:00
|
|
|
while(DI_GetStatus() & DVD_INIT) usleep(10);
|
2008-12-10 19:16:30 +01:00
|
|
|
if (!(DI_GetStatus() & DVD_READY))
|
|
|
|
{
|
2010-05-28 14:08:00 +02:00
|
|
|
char msg[64];
|
2009-05-19 18:14:43 +02:00
|
|
|
sprintf(msg, "DI Status Error: 0x%08X !\n",DI_GetStatus());
|
2009-05-20 17:11:06 +02:00
|
|
|
GUI_WaitPrompt("Error",msg);
|
2008-12-10 19:16:30 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
DVD_Mount();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!getpvd())
|
|
|
|
{
|
2009-05-20 17:11:06 +02:00
|
|
|
GUI_WaitPrompt("Error","Disc can not be read !");
|
2008-12-10 19:16:30 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2009-05-19 18:14:43 +02:00
|
|
|
|
|
|
|
GUI_MsgBoxClose();
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
if (!haveDVDdir)
|
2008-12-10 19:16:30 +01:00
|
|
|
{
|
2010-05-07 20:25:27 +02:00
|
|
|
/* reset current directory */
|
2008-12-10 19:16:30 +01:00
|
|
|
rootdir = basedir;
|
|
|
|
|
2010-05-07 20:25:27 +02:00
|
|
|
/* parse current directory */
|
2008-12-10 19:16:30 +01:00
|
|
|
int max = DVD_ParseDirectory ();
|
2010-05-07 20:25:27 +02:00
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
if (max)
|
|
|
|
{
|
2010-05-07 20:25:27 +02:00
|
|
|
/* set DVD access flag */
|
2008-12-10 19:16:30 +01:00
|
|
|
haveDVDdir = 1;
|
|
|
|
|
|
|
|
/* reset File selector */
|
2009-05-26 15:14:33 +02:00
|
|
|
ClearSelector(max);
|
2010-05-07 20:25:27 +02:00
|
|
|
|
|
|
|
/* clear FAT access flag */
|
|
|
|
FAT_ClearDirectory();
|
|
|
|
|
2009-05-19 18:14:43 +02:00
|
|
|
return 1;
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no entries found */
|
2009-05-20 17:11:06 +02:00
|
|
|
GUI_WaitPrompt("Error","No files found !");
|
2010-05-07 20:25:27 +02:00
|
|
|
|
2008-12-10 19:16:30 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-19 18:14:43 +02:00
|
|
|
return 1;
|
2008-12-10 19:16:30 +01:00
|
|
|
}
|