mirror of https://github.com/PCSX2/pcsx2.git
527 lines
14 KiB
C++
527 lines
14 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 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 PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
#include "Common.h"
|
|
|
|
#include "GS.h" // for sending game crc to mtgs
|
|
#include "Elfheader.h"
|
|
|
|
using namespace std;
|
|
|
|
u32 ElfCRC;
|
|
u32 ElfEntry;
|
|
wxString LastELF;
|
|
|
|
#if 0
|
|
// fixme: ELF command line option system.
|
|
// It parses a command line and pastes it into PS2 memory, and then points the a0 register at it.
|
|
// A user-written ELF can then load the data and respond accordingly. Needs a rewrite using modern
|
|
// string parsing utils. :)
|
|
|
|
//2002-09-19 (Florin)
|
|
char args[256]="ez.m2v"; //to be accessed by other files
|
|
uptr args_ptr; //a big value; in fact, it is an address
|
|
|
|
static bool isEmpty(int addr)
|
|
{
|
|
return ((eeMem->Main[addr] == 0) || (eeMem->Main[addr] == 32));
|
|
}
|
|
|
|
//in a0 is passed the address of the command line args,
|
|
//i.e. a pointer to an area like this:
|
|
//+00 unknown/unused
|
|
//+04 argc; number of arguments
|
|
//+08 argv[0]; address of the first parameter - program name (char*) -
|
|
//+08 argv[1]; address of the second parameter (char*) |
|
|
//+0C argv[2]; and so on |
|
|
//........ |
|
|
//+08+4*argc the program name(first param) <--
|
|
//+08+4*argc+strlen(argv[0]+1) the rest of params; i.e. a copy of 'args'
|
|
// see above 'char args[256];'
|
|
|
|
// The results of this function will normally be that it finds an arg at 13 chars, and another at 0.
|
|
// It'd probably be easier to 0 out all 256 chars, split args, copy all the arguments in, and note
|
|
// the locations of each split... --arcum42
|
|
|
|
static uint parseCommandLine( const wxString& filename )
|
|
{
|
|
if ( ( args_ptr != 0xFFFFFFFF ) && ( args_ptr > (4 + 4 + 256) ) )
|
|
{
|
|
const char *p;
|
|
int argc, i, ret = 0;
|
|
u32 args_end;
|
|
|
|
args_ptr -= 256;
|
|
|
|
args[ 255 ] = 0;
|
|
|
|
// Copy the parameters into the section of memory at args_ptr,
|
|
// then zero out anything past the end of args till 256 chars is reached.
|
|
memcpy( &eeMem->Main[ args_ptr ], args, 256 );
|
|
memset( &eeMem->Main[ args_ptr + strlen( args ) ], 0, 256 - strlen( args ) );
|
|
args_end = args_ptr + strlen( args );
|
|
|
|
// Set p to just the filename, no path.
|
|
#ifdef _WIN32
|
|
p = strrchr( filename, '\\' );
|
|
#else //linux
|
|
p = strrchr( filename, '/' );
|
|
if( p == NULL ) p = strchr(filename, '\\');
|
|
#endif
|
|
if (p)
|
|
p++;
|
|
else
|
|
p = filename;
|
|
|
|
|
|
args_ptr -= strlen( p ) + 1;
|
|
|
|
//fill param 0; i.e. name of the program
|
|
strcpy( (char*)&eeMem->Main[ args_ptr ], p );
|
|
|
|
// Start from the end of where we wrote to, not including all the zero'd out area.
|
|
for ( i = args_end - args_ptr + 1, argc = 0; i > 0; i-- )
|
|
{
|
|
while (i && isEmpty(args_ptr + i )) { i--; }
|
|
|
|
// If the last char is a space, set it to 0.
|
|
if ( eeMem->Main[ args_ptr + i + 1 ] == ' ') eeMem->Main[ args_ptr + i + 1 ] = 0;
|
|
|
|
while (i && !isEmpty(args_ptr + i )) { i--; }
|
|
|
|
// Now that we've gone back a word, increase the number of arguments,
|
|
// and mark the location of the argument.
|
|
if (!isEmpty(args_ptr + i )) // i <= 0
|
|
{
|
|
// If the spot we are on is not a space or null , use it.
|
|
argc++;
|
|
ret = args_ptr - 4 - 4 - argc * 4;
|
|
|
|
if (ret < 0 ) return 0;
|
|
((u32*)eeMem->Main)[ args_ptr / 4 - argc ] = args_ptr + i;
|
|
}
|
|
else
|
|
{
|
|
if (!isEmpty(args_ptr + i + 1))
|
|
{
|
|
// Otherwise, use the next character .
|
|
argc++;
|
|
ret = args_ptr - 4 - 4 - argc * 4;
|
|
|
|
if (ret < 0 ) return 0;
|
|
((u32*)eeMem->Main)[ args_ptr / 4 - argc ] = args_ptr + i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pass the number of arguments, and if we have arguments.
|
|
((u32*)eeMem->Main)[ args_ptr /4 - argc - 1 ] = argc; //how many args
|
|
((u32*)eeMem->Main)[ args_ptr /4 - argc - 2 ] = ( argc > 0); //have args? //not used, cannot be filled at all
|
|
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
//---------------
|
|
|
|
// All of ElfObjects functions.
|
|
ElfObject::ElfObject(const wxString& srcfile, IsoFile isofile)
|
|
: filename( srcfile )
|
|
|
|
, data( wxULongLong(isofile.getLength()).GetLo(), L"ELF headers" )
|
|
, header( *(ELF_HEADER*)data.GetPtr() )
|
|
, proghead( NULL )
|
|
, secthead( NULL )
|
|
{
|
|
isCdvd = true;
|
|
checkElfSize(data.GetSizeInBytes());
|
|
readIso(isofile);
|
|
initElfHeaders();
|
|
}
|
|
|
|
ElfObject::ElfObject( const wxString& srcfile, uint hdrsize )
|
|
: filename( srcfile )
|
|
, data( wxULongLong(hdrsize).GetLo(), L"ELF headers" )
|
|
, header( *(ELF_HEADER*)data.GetPtr() )
|
|
, proghead( NULL )
|
|
, secthead( NULL )
|
|
{
|
|
isCdvd = false;
|
|
checkElfSize(data.GetSizeInBytes());
|
|
readFile();
|
|
initElfHeaders();
|
|
}
|
|
|
|
void ElfObject::initElfHeaders()
|
|
{
|
|
DevCon.WriteLn( L"Initializing Elf: %d bytes", data.GetSizeInBytes());
|
|
|
|
if ( header.e_phnum > 0 )
|
|
proghead = (ELF_PHR*)&data[header.e_phoff];
|
|
|
|
if ( header.e_shnum > 0 )
|
|
secthead = (ELF_SHR*)&data[header.e_shoff];
|
|
|
|
if ( ( header.e_shnum > 0 ) && ( header.e_shentsize != sizeof(ELF_SHR) ) )
|
|
Console.Error( "(ELF) Size of section headers is not standard" );
|
|
|
|
if ( ( header.e_phnum > 0 ) && ( header.e_phentsize != sizeof(ELF_PHR) ) )
|
|
Console.Error( "(ELF) Size of program headers is not standard" );
|
|
|
|
//getCRC();
|
|
|
|
const char* elftype = NULL;
|
|
switch( header.e_type )
|
|
{
|
|
default:
|
|
ELF_LOG( "type: unknown = %x", header.e_type );
|
|
break;
|
|
|
|
case 0x0: elftype = "no file type"; break;
|
|
case 0x1: elftype = "relocatable"; break;
|
|
case 0x2: elftype = "executable"; break;
|
|
}
|
|
|
|
if (elftype != NULL) ELF_LOG( "type: %s", elftype );
|
|
|
|
const char* machine = NULL;
|
|
|
|
switch(header.e_machine)
|
|
{
|
|
case 1: machine = "AT&T WE 32100"; break;
|
|
case 2: machine = "SPARC"; break;
|
|
case 3: machine = "Intel 80386"; break;
|
|
case 4: machine = "Motorola 68000"; break;
|
|
case 5: machine = "Motorola 88000"; break;
|
|
case 7: machine = "Intel 80860"; break;
|
|
case 8: machine = "mips_rs3000"; break;
|
|
|
|
default:
|
|
ELF_LOG( "machine: unknown = %x", header.e_machine );
|
|
break;
|
|
}
|
|
|
|
if (machine != NULL) ELF_LOG( "machine: %s", machine );
|
|
|
|
ELF_LOG("version: %d",header.e_version);
|
|
ELF_LOG("entry: %08x",header.e_entry);
|
|
ELF_LOG("flags: %08x",header.e_flags);
|
|
ELF_LOG("eh size: %08x",header.e_ehsize);
|
|
ELF_LOG("ph off: %08x",header.e_phoff);
|
|
ELF_LOG("ph entsiz: %08x",header.e_phentsize);
|
|
ELF_LOG("ph num: %08x",header.e_phnum);
|
|
ELF_LOG("sh off: %08x",header.e_shoff);
|
|
ELF_LOG("sh entsiz: %08x",header.e_shentsize);
|
|
ELF_LOG("sh num: %08x",header.e_shnum);
|
|
ELF_LOG("sh strndx: %08x",header.e_shstrndx);
|
|
|
|
ELF_LOG("\n");
|
|
|
|
//applyPatches();
|
|
}
|
|
|
|
bool ElfObject::hasProgramHeaders() { return (proghead != NULL); }
|
|
bool ElfObject::hasSectionHeaders() { return (secthead != NULL); }
|
|
bool ElfObject::hasHeaders() { return (hasProgramHeaders() && hasSectionHeaders()); }
|
|
|
|
void ElfObject::readIso(IsoFile file)
|
|
{
|
|
int rsize = file.read(data.GetPtr(), data.GetSizeInBytes());
|
|
if (rsize < data.GetSizeInBytes()) throw Exception::EndOfStream(filename);
|
|
}
|
|
|
|
void ElfObject::readFile()
|
|
{
|
|
int rsize = 0;
|
|
FILE *f;
|
|
|
|
f = fopen( filename.ToUTF8(), "rb" );
|
|
if (f == NULL) Exception::FileNotFound( filename );
|
|
|
|
fseek(f, 0, SEEK_SET);
|
|
rsize = fread(data.GetPtr(), 1, data.GetSizeInBytes(), f);
|
|
fclose( f );
|
|
|
|
if (rsize < data.GetSizeInBytes()) throw Exception::EndOfStream(filename);
|
|
}
|
|
|
|
static wxString GetMsg_InvalidELF()
|
|
{
|
|
return
|
|
_("Cannot load ELF binary image. The file may be corrupt or incomplete.") +
|
|
wxString(L"\n\n") +
|
|
_("If loading from an ISO image, this error may be caused by an unsupported ISO image type or a bug in PCSX2 ISO image support.");
|
|
}
|
|
|
|
|
|
void ElfObject::checkElfSize(s64 elfsize)
|
|
{
|
|
const wxChar* diagMsg = NULL;
|
|
if (elfsize > 0xfffffff) diagMsg = L"Illegal ELF file size over 2GB!";
|
|
else if (elfsize == -1) diagMsg = L"ELF file does not exist!";
|
|
else if (elfsize == 0) diagMsg = L"Unexpected end of ELF file.";
|
|
|
|
if (diagMsg)
|
|
throw Exception::BadStream(filename)
|
|
.SetDiagMsg(diagMsg)
|
|
.SetUserMsg(GetMsg_InvalidELF());
|
|
}
|
|
|
|
u32 ElfObject::getCRC()
|
|
{
|
|
u32 CRC = 0;
|
|
|
|
const u32* srcdata = (u32*)data.GetPtr();
|
|
for(u32 i=data.GetSizeInBytes()/4; i; --i, ++srcdata)
|
|
CRC ^= *srcdata;
|
|
|
|
return CRC;
|
|
}
|
|
|
|
void ElfObject::loadProgramHeaders()
|
|
{
|
|
if (proghead == NULL) return;
|
|
|
|
for( int i = 0 ; i < header.e_phnum ; i++ )
|
|
{
|
|
ELF_LOG( "Elf32 Program Header" );
|
|
ELF_LOG( "type: " );
|
|
|
|
switch(proghead[ i ].p_type)
|
|
{
|
|
default:
|
|
ELF_LOG( "unknown %x", (int)proghead[ i ].p_type );
|
|
break;
|
|
|
|
case 0x1:
|
|
{
|
|
ELF_LOG("load");
|
|
const uint elfsize = data.GetLength();
|
|
|
|
if (proghead[ i ].p_offset < elfsize)
|
|
{
|
|
int size;
|
|
|
|
if ((proghead[ i ].p_filesz + proghead[ i ].p_offset) > elfsize)
|
|
size = elfsize - proghead[ i ].p_offset;
|
|
else
|
|
size = proghead[ i ].p_filesz;
|
|
|
|
if (proghead[ i ].p_vaddr != proghead[ i ].p_paddr)
|
|
Console.Warning( "ElfProgram different load addrs: paddr=0x%8.8x, vaddr=0x%8.8x",
|
|
proghead[ i ].p_paddr, proghead[ i ].p_vaddr);
|
|
|
|
// used to be paddr
|
|
memcpy_fast(
|
|
&eeMem->Main[proghead[ i ].p_vaddr & 0x1ffffff],
|
|
data.GetPtr(proghead[ i ].p_offset), size
|
|
);
|
|
|
|
ELF_LOG("\t*LOADED*");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ELF_LOG("\n");
|
|
ELF_LOG("offset: %08x",proghead[i].p_offset);
|
|
ELF_LOG("vaddr: %08x",proghead[i].p_vaddr);
|
|
ELF_LOG("paddr: %08x",proghead[i].p_paddr);
|
|
ELF_LOG("file size: %08x",proghead[i].p_filesz);
|
|
ELF_LOG("mem size: %08x",proghead[i].p_memsz);
|
|
ELF_LOG("flags: %08x",proghead[i].p_flags);
|
|
ELF_LOG("palign: %08x",proghead[i].p_align);
|
|
ELF_LOG("\n");
|
|
}
|
|
}
|
|
|
|
void ElfObject::loadSectionHeaders()
|
|
{
|
|
if (secthead == NULL || header.e_shoff > (u32)data.GetLength()) return;
|
|
|
|
const u8* sections_names = data.GetPtr( secthead[ (header.e_shstrndx == 0xffff ? 0 : header.e_shstrndx) ].sh_offset );
|
|
|
|
int i_st = -1, i_dt = -1;
|
|
|
|
for( int i = 0 ; i < header.e_shnum ; i++ )
|
|
{
|
|
ELF_LOG( "ELF32 Section Header [%x] %s", i, §ions_names[ secthead[ i ].sh_name ] );
|
|
|
|
// used by parseCommandLine
|
|
//if ( secthead[i].sh_flags & 0x2 )
|
|
// args_ptr = min( args_ptr, secthead[ i ].sh_addr & 0x1ffffff );
|
|
|
|
ELF_LOG("\n");
|
|
|
|
const char* sectype = NULL;
|
|
switch(secthead[ i ].sh_type)
|
|
{
|
|
case 0x0: sectype = "null"; break;
|
|
case 0x1: sectype = "progbits"; break;
|
|
case 0x2: sectype = "symtab"; break;
|
|
case 0x3: sectype = "strtab"; break;
|
|
case 0x4: sectype = "rela"; break;
|
|
case 0x8: sectype = "no bits"; break;
|
|
case 0x9: sectype = "rel"; break;
|
|
|
|
default:
|
|
ELF_LOG("type: unknown %08x",secthead[i].sh_type);
|
|
break;
|
|
}
|
|
|
|
ELF_LOG("type: %s", sectype);
|
|
ELF_LOG("flags: %08x", secthead[i].sh_flags);
|
|
ELF_LOG("addr: %08x", secthead[i].sh_addr);
|
|
ELF_LOG("offset: %08x", secthead[i].sh_offset);
|
|
ELF_LOG("size: %08x", secthead[i].sh_size);
|
|
ELF_LOG("link: %08x", secthead[i].sh_link);
|
|
ELF_LOG("info: %08x", secthead[i].sh_info);
|
|
ELF_LOG("addralign: %08x", secthead[i].sh_addralign);
|
|
ELF_LOG("entsize: %08x", secthead[i].sh_entsize);
|
|
// dump symbol table
|
|
|
|
if (secthead[ i ].sh_type == 0x02)
|
|
{
|
|
i_st = i;
|
|
i_dt = secthead[i].sh_link;
|
|
}
|
|
}
|
|
|
|
if ((i_st >= 0) && (i_dt >= 0))
|
|
{
|
|
const char * SymNames;
|
|
Elf32_Sym * eS;
|
|
|
|
SymNames = (char*)data.GetPtr(secthead[i_dt].sh_offset);
|
|
eS = (Elf32_Sym*)data.GetPtr(secthead[i_st].sh_offset);
|
|
Console.WriteLn("found %d symbols", secthead[i_st].sh_size / sizeof(Elf32_Sym));
|
|
|
|
for(uint i = 1; i < (secthead[i_st].sh_size / sizeof(Elf32_Sym)); i++) {
|
|
if ((eS[i].st_value != 0) && (ELF32_ST_TYPE(eS[i].st_info) == 2))
|
|
{
|
|
R5900::disR5900AddSym(eS[i].st_value, &SymNames[eS[i].st_name]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ElfObject::loadHeaders()
|
|
{
|
|
loadProgramHeaders();
|
|
loadSectionHeaders();
|
|
}
|
|
|
|
// return value:
|
|
// 0 - Invalid or unknown disc.
|
|
// 1 - PS1 CD
|
|
// 2 - PS2 CD
|
|
int GetPS2ElfName( wxString& name )
|
|
{
|
|
int retype = 0;
|
|
|
|
try {
|
|
IsoFSCDVD isofs;
|
|
IsoFile file( isofs, L"SYSTEM.CNF;1");
|
|
|
|
int size = file.getLength();
|
|
if( size == 0 ) return 0;
|
|
|
|
while( !file.eof() )
|
|
{
|
|
const wxString original( fromUTF8(file.readLine().c_str()) );
|
|
const ParsedAssignmentString parts( original );
|
|
|
|
if( parts.lvalue.IsEmpty() && parts.rvalue.IsEmpty() ) continue;
|
|
if( parts.rvalue.IsEmpty() )
|
|
{
|
|
Console.Warning( "(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:" );
|
|
Console.Indent().WriteLn( original );
|
|
continue;
|
|
}
|
|
|
|
if( parts.lvalue == L"BOOT2" )
|
|
{
|
|
name = parts.rvalue;
|
|
Console.WriteLn( Color_StrongBlue, L"(SYSTEM.CNF) Detected PS2 Disc = " + name );
|
|
retype = 2;
|
|
}
|
|
else if( parts.lvalue == L"BOOT" )
|
|
{
|
|
name = parts.rvalue;
|
|
Console.WriteLn( Color_StrongBlue, L"(SYSTEM.CNF) Detected PSX/PSone Disc = " + name );
|
|
retype = 1;
|
|
}
|
|
else if( parts.lvalue == L"VMODE" )
|
|
{
|
|
Console.WriteLn( Color_Blue, L"(SYSTEM.CNF) Disc region type = " + parts.rvalue );
|
|
}
|
|
else if( parts.lvalue == L"VER" )
|
|
{
|
|
Console.WriteLn( Color_Blue, L"(SYSTEM.CNF) Software version = " + parts.rvalue );
|
|
}
|
|
}
|
|
|
|
if( retype == 0 )
|
|
{
|
|
Console.Error("(GetElfName) Disc image is *not* a Playstation or PS2 game!");
|
|
return 0;
|
|
}
|
|
}
|
|
catch( Exception::FileNotFound& )
|
|
{
|
|
//Console.Warning(ex.FormatDiagnosticMessage());
|
|
return 0; // no SYSTEM.CNF, not a PS1/PS2 disc.
|
|
}
|
|
catch (Exception::BadStream& ex)
|
|
{
|
|
Console.Error(ex.FormatDiagnosticMessage());
|
|
return 0; // ISO error
|
|
}
|
|
|
|
#ifdef PCSX2_DEVBUILD
|
|
FILE *fp;
|
|
int i;
|
|
char buffer[512];
|
|
|
|
fp = fopen("System.map", "r");
|
|
if( fp == NULL ) return 2;
|
|
|
|
u32 addr;
|
|
|
|
Console.WriteLn("Loading System.map...");
|
|
while (!feof(fp)) {
|
|
fseek(fp, 8, SEEK_CUR);
|
|
buffer[0] = '0'; buffer[1] = 'x';
|
|
for (i=2; i<10; i++) buffer[i] = fgetc(fp); buffer[i] = 0;
|
|
addr = strtoul(buffer, (char**)NULL, 0);
|
|
fseek(fp, 3, SEEK_CUR);
|
|
for (i=0; i<512; i++) {
|
|
buffer[i] = fgetc(fp);
|
|
if (buffer[i] == '\n' || buffer[i] == 0) break;
|
|
}
|
|
if (buffer[i] == 0) break;
|
|
buffer[i] = 0;
|
|
|
|
R5900::disR5900AddSym(addr, buffer);
|
|
}
|
|
fclose(fp);
|
|
#endif
|
|
|
|
return retype;
|
|
}
|