pcsx2/plugins/CDVDisoEFP/src/isofile.c

692 lines
20 KiB
C

/* isofile.c
* Copyright (C) 2002-2005 PCSX2 Team
*
* 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.
*
* 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.
*
* 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
*
* PCSX2 members can be contacted through their website at www.pcsx2.net.
*/
#include <stddef.h> // NULL
#include <stdlib.h> // malloc()
#include <sys/types.h> // off64_t
#ifndef __LINUX__
#ifdef __linux__
#define __LINUX__
#endif /* __linux__ */
#endif /* No __LINUX__ */
#define CDVDdefs
#include "PS2Edefs.h"
#include "logfile.h"
#include "multifile.h"
#include "isocompress.h"
#include "actualfile.h"
#include "imagetype.h"
#include "toc.h"
#include "ecma119.h"
#include "isofile.h"
const char *isofileext[] =
{
".iso",
".bin",
".img",
NULL
};
const char *cdname = "CD-XA001\0";
const char *playstationid = "PLAYSTATION\0";
// const char ps1/2name?
// Internal functions
void IsoNameStripExt(struct IsoFile *isofile)
{
int tempext;
int tempnamepos;
int tempextpos;
tempext = 0;
while (isofileext[tempext] != NULL)
{
tempextpos = 0;
while (*(isofileext[tempext] + tempextpos) != 0) tempextpos++;
tempnamepos = isofile->namepos;
while ((tempnamepos > 0) && (tempextpos > 0) &&
(isofile->name[tempnamepos - 1] == *(isofileext[tempext] + tempextpos - 1)))
{
tempnamepos--;
tempextpos--;
} // ENDWHILE- Comparing one extension to the end of the file name
if (tempextpos == 0)
{
isofile->namepos = tempnamepos; // Move name pointer in front of ext.
tempext = 0; // ... and test the list all over again.
}
else
{
tempext++; // Next ext in the list to test...
} // ENDIF- Did we find a match?
} // ENDWHILE- looking through extension list
} // END IsoNameStripExt()
// External functions
struct IsoFile *IsoFileOpenForRead(const char *filename)
{
struct IsoFile *newfile;
int retval;
int i;
char tempblock[2448];
struct tocTN toctn;
struct tocTD toctd;
// union {
// struct ECMA119PrimaryVolume vol;
// char ch[sizeof(struct ECMA119PrimaryVolume)];
// } *volcheck;
union
{
struct ECMA119PrimaryVolume *vol;
char *ch;
} volcheck;
newfile = NULL;
if (filename == NULL) return(NULL);
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileOpenForRead(%s)", filename);
#endif /* VERBOSE_FUNCTION_ISOFILE */
newfile = (struct IsoFile *) malloc(sizeof(struct IsoFile));
if (newfile == NULL) return(NULL);
newfile->sectorpos = 0;
newfile->openforread = 1; // Read-only ISO
newfile->filebytepos = 0;
newfile->filesectorpos = 0;
newfile->blocksize = 0; // Flags as non-detected yet (Compress vs. Image)
newfile->tabledata = NULL;
newfile->namepos = 0;
while ((newfile->namepos < 255) &&
(*(filename + newfile->namepos) != 0))
{
newfile->name[newfile->namepos] = *(filename + newfile->namepos);
newfile->namepos++;
} // ENDWHILE- copying the file name in...
newfile->name[newfile->namepos] = 0; // And 0-terminate.
IsoNameStripExt(newfile); // Ex: -I00.Z[.bin]
// File Compression name detection
newfile->compress = IsoNameStripCompress(newfile); // Ex: -I00.bin[.Z]
newfile->compresspos = newfile->namepos;
// Test File name compression
retval = -1;
if (newfile->compress > 0)
{
retval = CompressOpenForRead(newfile);
if (retval == -1) CompressClose(newfile);
} // ENDIF- Have a compression type hint? Test it out
if (retval == -1)
{
newfile->compress = 5;
while ((newfile->compress > 0) && (retval == -1))
{
retval = CompressOpenForRead(newfile);
if (retval == -1)
{
CompressClose(newfile);
newfile->compress--;
} // ENDIF- Failed to open? Close it... and try the next one.
} // ENDWHILE- Trying to find a compression scheme that will work...
if (newfile->compress == 0)
{
newfile->handle = ActualFileOpenForRead(newfile->name);
if (newfile->handle == ACTUALHANDLENULL)
{
free(newfile);
newfile = NULL;
return(NULL);
} // ENDIF- Failed to open? Abort.
newfile->filebytesize = ActualFileSize(newfile->handle);
} // ENDIF- No compression? Open it uncompressed.
} // ENDIF- Temp- failed to open? Abort...
// Compressed data file with no table? Return prematurely...
// Condition detection: compress > 0, tablehandle == ACTUALHANDLENULL
if (retval == -2)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: Data file with no table!");
#endif /* VERBOSE_FUNCTION_ISOFILE */
return(newfile);
} // ENDIF-
newfile->imagetype = DetectImageType(newfile);
if (newfile->compress == 0)
{
newfile->filesectorsize = newfile->filebytesize / newfile->blocksize;
} // ENDIF- Now that blocksize is known, raw file sectors can be figured out
IsoNameStripExt(newfile); // Ex: -I00[.bin].Z
IsoNameStripMulti(newfile); // Ex: [-I00].bin.Z
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD isofile: Filename: %s", filename);
if (newfile->multi > 0) PrintLog("CDVD isofile: Multiple <2GB files.");
PrintLog("CDVD isofile: Compression Method: %s",
compressdesc[newfile->compress]);
PrintLog("CDVD isofile: Image Type: %s",
newfile->imagename);
PrintLog("CDVD isofile: Block Size: %lli", newfile->blocksize);
PrintLog("CDVD isofile: Total Sectors (of first file): %lli",
newfile->filesectorsize);
#endif /* VERBOSE_DISC_INFO */
// Load a TOC from a .toc file (is there is one)
retval = IsoLoadTOC(newfile);
if (retval == 0) return(newfile);
// Get the volume sector for disc type test
retval = IsoFileSeek(newfile, 16);
if (retval < 0)
{
newfile = IsoFileClose(newfile);
return(NULL);
} // ENDIF- Could not find the directory sector? Abort.
retval = IsoFileRead(newfile, tempblock);
if (retval < 0)
{
newfile = IsoFileClose(newfile);
return(NULL);
} // ENDIF- Could not read the directory sector? Abort.
volcheck.ch = tempblock;
volcheck.ch += newfile->blockoffset;
if (ValidateECMA119PrimaryVolume(volcheck.vol) != 0)
{
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD isofile: Not an ISO9660 disc! Music CD perhaps?");
#endif /* VERBOSE_DISC_INFO */
newfile->cdvdtype = CDVD_TYPE_CDDA;
}
else
{
// Is this a playstation image?
i = 0;
while ((*(playstationid + i) != 0) &&
(*(playstationid + i) == tempblock[newfile->blockoffset + 8 + i])) i++;
if (*(playstationid + i) != 0)
{
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD isofile: Not a Playstation Disc!");
#endif /* VERBOSE_DISC_INFO */
newfile->cdvdtype = CDVD_TYPE_DVDV;
}
else
{
newfile->cdvdtype = CDVD_TYPE_PS2DVD;
} // ENDIF- Is this not a Playstation 1 image?
// Sidenote: if the emulator is just playing Playstation 2 images, we could
// just invalidate the image file right here.
} // ENDIF- Not an ISO9660 disc? Assume Music CD.
if (newfile->cdvdtype == CDVD_TYPE_PS2DVD)
{
// Is this a Playstation CD image?
i = 0;
while ((*(cdname + i) != 0) &&
(*(cdname + i) == tempblock[newfile->blockoffset + 1024 + i])) i++;
if (*(cdname + i) == 0)
{
newfile->cdvdtype = CDVD_TYPE_PSCD;
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD isofile: Image is a Playstation 1 CD.");
#endif /* VERBOSE_DISC_INFO */
}
else
{
if (newfile->blocksize != 2048)
{
newfile->cdvdtype = CDVD_TYPE_PS2CD;
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD isofile: Image is a Playstation 2 CD.");
#endif /* VERBOSE_DISC_INFO */
}
else
{
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD isofile: Image is a DVD.");
#endif /* VERBOSE_DISC_INFO */
} // ENDIF- Is the blocksize not 2048? CD image then.
} // ENDIF- Is this a PS1 CD image?
} // ENDIF- Is this a Playstation image?
volcheck.ch = NULL;
if ((newfile->cdvdtype == CDVD_TYPE_DVDV) &&
(newfile->blocksize == 2352)) newfile->cdvdtype = CDVD_TYPE_CDDA;
// Slap together a TOC based on the above guesswork.
IsoInitTOC(newfile);
if ((newfile->cdvdtype != CDVD_TYPE_PS2DVD) &&
(newfile->cdvdtype != CDVD_TYPE_DVDV))
{
toctn.strack = 1;
toctn.etrack = 1;
IsoAddTNToTOC(newfile, toctn);
toctd.type = 0;
toctd.lsn = newfile->filesectorsize;
IsoAddTDToTOC(newfile, 0xAA, toctd);
toctd.type = 0; // ?
if (newfile->cdvdtype == CDVD_TYPE_CDDA)
{
toctd.type = CDVD_AUDIO_TRACK; // Music track assumed
}
else
{
toctd.type = CDVD_MODE1_TRACK; // Data track assumed
} // ENDIF- Is this track a music or data track?
toctd.lsn = 0;
IsoAddTDToTOC(newfile, 1, toctd);
} // ENDIF- Is this a CD? Single track for all sectors
return(newfile);
} // END IsoFileOpenForRead()
int IsIsoFile(const char *filename)
{
int retval;
struct IsoFile *tempfile;
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsIsoFile()");
#endif /* VERBOSE_FUNCTION_ISOFILE */
retval = IsActualFile(filename);
if (retval < 0) return(retval); // Not a regular file? Report it.
tempfile = NULL;
tempfile = IsoFileOpenForRead(filename);
if (tempfile == NULL) return(-3); // Not an image file? Report it.
retval = 0;
if ((tempfile->compress > 0) &&
(tempfile->tablehandle == ACTUALHANDLENULL) &&
(tempfile->tabledata == NULL)) retval = -4;
tempfile = IsoFileClose(tempfile);
return(retval);
} // END IsIsoFile()
int IsoFileSeek(struct IsoFile *file, off64_t sector)
{
int retval;
if (file == NULL) return(-1);
if (sector < 0) return(-1);
if (sector == file->sectorpos) return(0);
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileSeek(%llu)", sector);
#endif /* VERBOSE_FUNCTION_ISOFILE */
if (file->multi > 0)
{
retval = MultiFileSeek(file, sector);
}
else if (file->compress > 0)
{
retval = CompressSeek(file, sector);
}
else
{
retval = ActualFileSeek(file->handle,
(sector * file->blocksize) + file->imageheader);
if (retval == 0)
{
file->filesectorpos = sector;
file->filebytepos = (sector * file->blocksize) + file->imageheader;
} // ENDIF- Succeeded? Adjust internal pointers
} // ENDLONGIF- Seek right file? Or compressed block? Or Raw block?
if (retval < 0)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: Trouble finding the sector!");
#endif /* VERBOSE_FUNCTION_ISOFILE */
return(-1);
} // ENDIF- Trouble reading the block? Say so!
file->sectorpos = sector;
return(0);
} // END IsoFileSeek()
int IsoFileRead(struct IsoFile *file, char *block)
{
int retval;
if (file == NULL) return(-1);
if (block == NULL) return(-1);
if (file->openforread == 0) return(-1);
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileRead(%i)", file->blocksize);
#endif /* VERBOSE_FUNCTION_ISOFILE */
if (file->multi > 0)
{
retval = MultiFileRead(file, block);
}
else if (file->compress > 0)
{
retval = CompressRead(file, block);
}
else
{
if (file->sectorpos >= file->filesectorsize) return(-1);
retval = ActualFileRead(file->handle,
file->blocksize,
block);
if (retval > 0) file->filebytepos += retval;
if (retval == file->blocksize) file->filesectorpos++;
} // ENDLONGIF- Read right file? Or compressed block? Or Raw block?
if (retval < 0)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: Trouble reading the sector!");
#endif /* VERBOSE_FUNCTION_ISOFILE */
return(-1);
} // ENDIF- Trouble reading the block? Say so!
if (retval < file->blocksize)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: Short block! Got %i out of %i bytes",
retval, file->blocksize);
#endif /* VERBOSE_FUNCTION_ISOFILE */
return(-1);
} // ENDIF- Didn't get enough bytes? Say so!
file->sectorpos++;
return(0);
} // END IsoFileRead()
struct IsoFile *IsoFileClose(struct IsoFile *file)
{
if (file == NULL) return(NULL);
if (file->handle != ACTUALHANDLENULL)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileClose()");
#endif /* VERBOSE_FUNCTION_ISOFILE */
if (file->compress > 0)
{
CompressClose(file);
}
else
{
ActualFileClose(file->handle);
file->handle = ACTUALHANDLENULL;
} // ENDIF- Compressed File? Close (and flush) compression too.
} // ENDIF- Open Handle? Close the file
free(file);
return(NULL);
} // END IsoFileClose()
struct IsoFile *IsoFileOpenForWrite(const char *filename,
int imagetype,
int multi,
int compress)
{
struct IsoFile *newfile;
newfile = NULL;
if (filename == NULL) return(NULL);
if ((imagetype < 0) || (imagetype > 11)) return(NULL);
if ((compress < 0) || (compress > 5)) return(NULL);
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileOpenForWrite()");
#endif /* VERBOSE_FUNCTION_ISOFILE */
newfile = (struct IsoFile *) malloc(sizeof(struct IsoFile));
if (newfile == NULL) return(NULL);
newfile->sectorpos = 0;
newfile->openforread = 0; // Write-only file
newfile->filebytesize = 0;
newfile->filebytepos = 0;
newfile->filesectorsize = 0;
newfile->filesectorpos = 0;
// if(toc != NULL) {
// for(i = 0; i < 2048; i++) newfile->toc[i] = *(toc + i);
// } else {
// for(i = 0; i < 2048; i++) newfile->toc[i] = 0;
// } // ENDIF- Do we have a PS2 Table of Contents to save out as well?
newfile->namepos = 0;
while ((newfile->namepos < 255) &&
(*(filename + newfile->namepos) != 0))
{
newfile->name[newfile->namepos] = *(filename + newfile->namepos);
newfile->namepos++;
} // ENDWHILE- copying the file name in...
newfile->name[newfile->namepos] = 0; // And 0-terminate.
IsoNameStripExt(newfile);
IsoNameStripCompress(newfile);
IsoNameStripExt(newfile);
IsoNameStripMulti(newfile);
newfile->name[newfile->namepos] = 0; // And 0-terminate.
newfile->imagetype = imagetype;
GetImageType(newfile, imagetype);
newfile->cdvdtype = CDVD_TYPE_PS2DVD; // Does it matter here? Nope.
newfile->multi = multi;
if (newfile->multi > 0)
{
newfile->name[newfile->namepos + 0] = '-';
newfile->name[newfile->namepos + 1] = 'I';
newfile->name[newfile->namepos + 2] = '0';
newfile->name[newfile->namepos + 3] = '0';
newfile->name[newfile->namepos + 4] = 0;
newfile->multipos = newfile->namepos + 3;
newfile->namepos += 4;
newfile->multistart = 0;
newfile->multiend = 0;
newfile->multinow = 0;
newfile->multioffset = 0;
newfile->multisectorend[0] = 0;
} // ENDIF- Are we creating a multi-file?
newfile->compress = compress;
switch (newfile->compress)
{
case 1:
case 3:
newfile->name[newfile->namepos + 0] = '.';
newfile->name[newfile->namepos + 1] = 'Z';
newfile->name[newfile->namepos + 2] = 0;
newfile->namepos += 2;
break;
case 2:
newfile->name[newfile->namepos + 0] = '.';
newfile->name[newfile->namepos + 1] = 'd';
newfile->name[newfile->namepos + 2] = 'u';
newfile->name[newfile->namepos + 3] = 'm';
newfile->name[newfile->namepos + 4] = 'p';
newfile->name[newfile->namepos + 5] = 0;
newfile->namepos += 5;
break;
case 4:
newfile->name[newfile->namepos + 0] = '.';
newfile->name[newfile->namepos + 1] = 'B';
newfile->name[newfile->namepos + 2] = 'Z';
newfile->name[newfile->namepos + 3] = '2';
newfile->name[newfile->namepos + 4] = 0;
newfile->namepos += 4;
break;
case 5:
newfile->name[newfile->namepos + 0] = '.';
newfile->name[newfile->namepos + 1] = 'b';
newfile->name[newfile->namepos + 2] = 'z';
newfile->name[newfile->namepos + 3] = '2';
newfile->name[newfile->namepos + 4] = 0;
newfile->namepos += 4;
break;
case 0:
default:
break;
} // ENDSWITCH compress- which compression extension should we add on?
newfile->name[newfile->namepos + 0] = '.';
newfile->name[newfile->namepos + 4] = 0;
if (newfile->blocksize == 2048)
{
newfile->name[newfile->namepos + 1] = 'i';
newfile->name[newfile->namepos + 2] = 's';
newfile->name[newfile->namepos + 3] = 'o';
}
else
{
newfile->name[newfile->namepos + 1] = 'b';
newfile->name[newfile->namepos + 2] = 'i';
newfile->name[newfile->namepos + 3] = 'n';
} // ENDIF- Is this a true ISO (or just a raw BIN file?)
newfile->namepos += 4;
if (IsActualFile(newfile->name) == 0)
{
free(newfile);
newfile = NULL;
return(NULL);
} // ENDIF- Does the destination file already exist?
if (newfile->compress > 0)
{
CompressOpenForWrite(newfile);
if ((newfile->handle != ACTUALHANDLENULL) &&
(newfile->tablehandle == ACTUALHANDLENULL))
{
ActualFileClose(newfile->handle);
newfile->handle = ACTUALHANDLENULL;
} // ENDIF Data file created, but table file stopped? Close and remove data
}
else
{
newfile->handle = ActualFileOpenForWrite(newfile->name);
} // ENDIF- Writing out a compressed file?
if (newfile->handle == ACTUALHANDLENULL)
{
free(newfile);
newfile = NULL;
return(NULL);
} // ENDIF- Couldn't create file? Abort
return(newfile);
} // END IsoFileOpenForWrite()
int IsoFileWrite(struct IsoFile *file, char *block)
{
int byteswritten;
if (file == NULL) return(-1);
if (block == NULL) return(-1);
if (file->openforread == 1) return(-1);
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileWrite()");
#endif /* VERBOSE_FUNCTION_ISOFILE */
byteswritten = 0;
if (file->multi > 0)
{
byteswritten = MultiFileWrite(file, block);
}
else if (file->compress > 0)
{
byteswritten = CompressWrite(file, block);
}
else
{
byteswritten = ActualFileWrite(file->handle,
file->blocksize,
block);
if (byteswritten > 0) file->filebytepos += byteswritten;
if (byteswritten == file->blocksize) file->filesectorpos++;
} // ENDLONGIF- Write to different file? Compressed block? or Raw?
if (byteswritten < 0)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: Trouble writing the sector!");
#endif /* VERBOSE_FUNCTION_ISOFILE */
return(-1);
} // ENDIF- Trouble reading the block? Say so!
if (file->filebytepos > file->filebytesize)
file->filebytesize = file->filebytepos;
if (byteswritten < file->blocksize)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: Short block! Wrote %i out of %i bytes",
byteswritten, file->blocksize);
#endif /* VERBOSE_FUNCTION_ISOFILE */
return(-1);
} // ENDIF- Didn't write enough bytes? Say so!
if (file->filesectorpos > file->filesectorsize)
file->filesectorsize = file->filesectorpos;
file->sectorpos++;
return(0);
} // END IsoFileWrite()
struct IsoFile *IsoFileCloseAndDelete(struct IsoFile *file)
{
int i;
if (file == NULL) return(NULL);
if (file->handle != ACTUALHANDLENULL)
{
#ifdef VERBOSE_FUNCTION_ISOFILE
PrintLog("CDVD isofile: IsoFileCloseAndDelete()");
#endif /* VERBOSE_FUNCTION_ISOFILE */
if (file->compress > 0)
{
CompressClose(file);
}
else
{
ActualFileClose(file->handle);
file->handle = ACTUALHANDLENULL;
} // ENDIF- Compressed File? Close (and flush) compression too.
} // ENDIF- Open Handle? Close the file
if (file->multi == 1)
{
for (i = file->multistart; i <= file->multiend; i++)
{
file->name[file->multipos] = '0' + i;
ActualFileDelete(file->name);
if (file->compress > 0)
{
file->tablename[file->multipos] = '0' + i;
ActualFileDelete(file->tablename);
} // ENDIF- Get the table file too?
} // NEXT i- iterate through each multi-file name, removing it.
}
else
{
ActualFileDelete(file->name);
if (file->compress > 0)
{
ActualFileDelete(file->tablename);
} // ENDIF- Get the table file too?
} // ENDIF- Do we have to remove multiple files?
free(file);
return(NULL);
} // END IsoFileCloseAndDelete()