Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ using Cheat Device.
## Features
* Easy to navigate menu system similar to CodeBreaker
* Supports booting retail discs and loader ELFs
* HDD build: boot ELFs stored on PFS partitions with boot paths like
`hdd0:__common:pfs:/APPS/OPNPS2LD.ELF` (requires starting Cheat Device from the HDD)
* Fast loading of large cheat lists
* Save manager for backing up and restoring game saves to/from a flash drive
* Powered by ps2rd's powerful cheat engine
Expand Down
2 changes: 2 additions & 0 deletions bootstrap/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ EE_OBJS = main.o
#EE_LDFLAGS = -s -Ttext 0x000F3000
#EE_LIBS += -lkernel -lpatches -lerl

EE_LIBS += -lfileXio

EE_CFLAGS := -mips3 -ffreestanding -fno-builtin -G0 \
-fshort-double -mlong64 -mhard-float -mno-abicalls -O2 -EL -Wall \
$(EE_INCS) $(EE_CFLAGS)
Expand Down
105 changes: 105 additions & 0 deletions bootstrap/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <iopheap.h>
#include <loadfile.h>
#include <sifrpc.h>
#include <fileXio_rpc.h>
#include <string.h>
#include <stdio.h>

Expand Down Expand Up @@ -68,6 +69,102 @@ typedef struct {
u32 align;
} elf_pheader_t;

/* Loads an ELF from a PFS partition that Cheat Device left mounted on
* "pfs0:" before executing the bootstrap. SifLoadElf and fio can't see
* iomanX devices like pfs, so the ELF is loaded manually through the
* fileXio RPC, whose IOP module is still resident since the IOP was not
* reset. elfpath is in the "hdd0:partition:pfs:/path" form and is passed
* as-is to the loaded ELF as argv[0]. Only returns on failure, leaving
* the current stage's debug color on screen. */
static void LoadElfFromPFS(char *elfpath)
{
elf_header_t boot_header;
elf_pheader_t boot_pheader;
char pfspath[256];
char *file;
char *args[1];
int i, fd;

GS_BGCOLOUR = red; /* RED: Opening elf */

/* Accept both the uLaunchELF-style ":pfs:" marker and the OPL/
elf-loader-style ":pfs0:" one. */
file = strstr(elfpath, ":pfs");
if (file == NULL)
return;
file += 4;
while (*file >= '0' && *file <= '9')
file++;
if (*file != ':')
return;
file++;

strcpy(pfspath, "pfs0:");
strncat(pfspath, file, sizeof(pfspath) - sizeof("pfs0:"));

SifInitRpc(0);
if (fileXioInit() < 0)
return;

fd = fileXioOpen(pfspath, O_RDONLY, 0);
if (fd < 0)
return;

GS_BGCOLOUR = blue; /* BLUE: Installing elf header */
if (fileXioRead(fd, (void *)&boot_header, sizeof(elf_header_t)) != sizeof(elf_header_t)) {
fileXioClose(fd);
return;
}

GS_BGCOLOUR = white;
/* check ELF magic */
if ((*(u32*)boot_header.ident) != ELF_MAGIC) {
fileXioClose(fd);
return;
}

GS_BGCOLOUR = green; /* GREEN: Reading elf */
/* copy loadable program segments to RAM */
for (i = 0; i < boot_header.phnum; i++) {
fileXioLseek(fd, boot_header.phoff + (i * sizeof(elf_pheader_t)), SEEK_SET);
fileXioRead(fd, (void *)&boot_pheader, sizeof(elf_pheader_t));

if (boot_pheader.type != ELF_PT_LOAD)
continue;

fileXioLseek(fd, boot_pheader.offset, SEEK_SET);
fileXioRead(fd, boot_pheader.vaddr, boot_pheader.filesz);

if (boot_pheader.memsz > boot_pheader.filesz)
memset(boot_pheader.vaddr + boot_pheader.filesz, 0,
boot_pheader.memsz - boot_pheader.filesz);
}

fileXioClose(fd);
fileXioUmount("pfs0:");

GS_BGCOLOUR = yellow; /* YELLOW: ExecPS2 */

/* IOP reboot routine from ps2rd */
while (!SifIopReset("rom0:UDNL rom0:EELOADCNF", 0))
;
while (!SifIopSync())
;

/* exit services */
fioExit();
SifExitIopHeap();
SifLoadFileExit();
SifExitRpc();
SifExitCmd();

FlushCache(0);
FlushCache(2);

args[0] = elfpath;
ExecPS2((u32*)boot_header.entry, 0, 1, args);
}

/* From NetCheat */
void MyLoadElf(char *elfpath)
{
Expand All @@ -88,6 +185,14 @@ void MyLoadElf(char *elfpath)
/* Clear scratchpad memory */
memset((void*)0x70000000, 0, 16 * 1024);

/* ELFs on the HDD live on PFS partitions, which SifLoadElf and fio
can't read. Load them through fileXio instead. */
if (elfpath[0] == 'h' && elfpath[1] == 'd' && elfpath[2] == 'd')
{
LoadElfFromPFS(elfpath); /* only returns on failure */
SleepThread();
}

/* HACK do not reset IOP when launching ELF from mass */
if (!(elfpath[0] == 'm' && elfpath[1] == 'a' &&
elfpath[2] == 's' && elfpath[3] == 's'))
Expand Down
11 changes: 8 additions & 3 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,19 @@ int main(int argc, char *argv[])
#endif
#ifdef HDD
DPRINTF("Checking if booting from HDD\n");
if (argc > 0) booting_from_hdd = (strstr(argv[0], "hdd0:")!=NULL)&&(strstr(argv[0], ":pfs:")!=NULL);
// uLaunchELF-style launchers pass "hdd0:partition:pfs:/path/file.elf"
// while OPL (through ps2sdk's elf-loader) passes
// "hdd0:partition:pfs0:path/file.elf", so accept both pfs markers.
if (argc > 0) booting_from_hdd = (strstr(argv[0], "hdd0:")!=NULL)&&(pfsPathGetFilePart(argv[0])!=NULL);
DPRINTF("Booting from hdd:%d\n", booting_from_hdd);
if (booting_from_hdd) {
char* BUF = NULL;
BUF = strdup(argv[0]); //use strdup, otherwise, path will become `hdd0:`
if (BUF==NULL) {
DPRINTF("Could not strdup()\n");
}
if (getMountInfo(BUF, NULL, MountPoint, pfspath)) {

DPRINTF("MountPoint '%s'\npfspath '%s'\n", MountPoint, pfspath);
char *pos = strrchr(pfspath, '/');
if (pos != NULL) {
Expand All @@ -61,12 +65,13 @@ int main(int argc, char *argv[])
chdir(pfspath);
} else displayError("Error processing HDD boot path (2)");
} else displayError("Error processing HDD boot path (1)");
}
#endif

ret = loadModules(booting_from_hdd);
if (ret != 0) displayError(error);
#ifdef HDD
if (ret == 0) {
if (ret == 0 && booting_from_hdd) {
int mtret=0;
if ((mtret=fileXioMount("pfs0:", MountPoint, FIO_MT_RDWR)) < 0) {
sprintf(error, "Error: failed to mount partition \"%s\"!\nerr:%d (0x%x)", MountPoint, mtret, mtret);
Expand Down
84 changes: 82 additions & 2 deletions src/startgame.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
#include "util.h"
#include "objectpool.h"

#ifdef HDD
#include <fileXio_rpc.h>

extern int booting_from_hdd;
extern int HDD_USABLE;
extern char MountPoint[40];
int getMountInfo(char *path, char *mountString, char *mountPoint, char *newCWD);
#endif

typedef struct {
u8 ident[16]; // struct definition for ELF object header
u16 type;
Expand Down Expand Up @@ -86,10 +95,66 @@ static void* loadBootstrap()
return (void *)eh->entry;
}

#ifdef HDD
// Prepares booting an ELF from an "hdd0:partition:pfs:/path" style boot path:
// remounts pfs0: on the target partition and checks that the ELF exists, so
// failures show an error menu here instead of hanging in the bootstrap.
// On failure, pfs0: is restored to the boot partition and 0 is returned.
static int setupHddBootPath(const char *path)
{
char msg[192];
char pathCopy[100];
char partition[64];
char pfsPath[128];
const char *file;
int ret;

if(!booting_from_hdd || !HDD_USABLE)
{
displayError("HDD boot paths can only be used when\nCheat Device was started from the HDD.");
return 0;
}

file = pfsPathGetFilePart(path);
strncpy(pathCopy, path, sizeof(pathCopy) - 1);
pathCopy[sizeof(pathCopy) - 1] = '\0';

if(!file || !getMountInfo(pathCopy, NULL, partition, NULL))
{
displayError("Invalid HDD boot path!\nExpected format:\nhdd0:partition:pfs:/path/to/boot.elf");
return 0;
}

fileXioUmount("pfs0:");
ret = fileXioMount("pfs0:", partition, FIO_MT_RDONLY);
if(ret < 0)
{
snprintf(msg, sizeof(msg), "Error: failed to mount partition\n\"%s\" (%d)", partition, ret);
fileXioMount("pfs0:", MountPoint, FIO_MT_RDWR);
displayError(msg);
return 0;
}

snprintf(pfsPath, sizeof(pfsPath), "pfs0:%s", file);
ret = fileXioOpen(pfsPath, O_RDONLY, 0);
if(ret < 0)
{
snprintf(msg, sizeof(msg), "Error: couldn't open \"%s\"\non partition \"%s\" (%d)", file, partition, ret);
fileXioUmount("pfs0:");
fileXioMount("pfs0:", MountPoint, FIO_MT_RDWR);
displayError(msg);
return 0;
}
fileXioClose(ret);

return 1;
}
#endif

void startgameExecute(const char *path)
{
static char boot2[100];

if(strcmp(path, "==Disc==") == 0)
{
if(!discPrompt())
Expand Down Expand Up @@ -143,11 +208,26 @@ void startgameExecute(const char *path)
}
else
{
strncpy(boot2, path, 100);
strncpy(boot2, path, sizeof(boot2) - 1);
boot2[sizeof(boot2) - 1] = '\0';
}

// Databases and settings must be saved before an HDD boot path gets a
// chance to remount pfs0: on another partition.
cheatsSaveDatabase();
settingsSave(NULL, 0);

if(strncmp(boot2, "hdd0:", 5) == 0)
{
#ifdef HDD
if(!setupHddBootPath(boot2))
return;
#else
displayError("HDD boot paths are only supported by\nthe HDD build of Cheat Device.");
return;
#endif
}

cheatsInstallCodesForEngine();
killMenus();
killCheats();
Expand Down
28 changes: 27 additions & 1 deletion src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "cheats.h"
#include "settings.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <loadfile.h>
#include <iopcontrol.h>
Expand All @@ -18,7 +19,7 @@
#include <libmc.h>
#include <unistd.h>

extern char* error;
extern char error[255];
#define EXTERN_BIN2O(_name_) extern u8 _name_##_start[]; extern int _name_##_size;
#define LOAD_IRX_BUF(_irx_, ARGC, ARGV, RET) SifExecModuleBuffer(_irx_##_start, _irx_##_size, ARGC, ARGV, RET)
#define LOAD_IRX_BUF_NARG(_irx_, RET) LOAD_IRX_BUF(_irx_, 0, NULL, RET)
Expand Down Expand Up @@ -105,9 +106,28 @@ void poweroffCallback(void *arg)
}
#endif

const char *pfsPathGetFilePart(const char *path)
{
const char *p = strstr(path, ":pfs");
if(!p)
return NULL;

p += strlen(":pfs");
while(*p >= '0' && *p <= '9')
p++; // Skip the mount point number, if any

if(*p != ':')
return NULL;

return p + 1;
}

int loadModules(int booting_from_hdd)
{
int ID, RET, HDDSTAT, filexio_loaded=0, dev9_loaded=0, mmceman_loaded=0;
#ifdef DEV9
int dev9_id=0, dev9_ret=0;
#endif
DPRINTF("\n ** Loading main modules **\n");

/* IOP reset routine taken from ps2rd */
Expand Down Expand Up @@ -151,6 +171,8 @@ int loadModules(int booting_from_hdd)
if (booting_from_hdd) {
ID = LOAD_IRX_BUF_NARG(_ps2dev9_irx, &RET);
dev9_loaded = IRX_LOAD_SUCCESS();
dev9_id = ID;
dev9_ret = RET;
}
#endif

Expand Down Expand Up @@ -240,7 +262,11 @@ int loadModules(int booting_from_hdd)
}
} else {
sprintf(error, "HDD Init Error:\nHDD Status: %d (%s)", HDDSTAT, HDDerr(HDDSTAT));
return -6;
}
} else {
sprintf(error, "HDD Init error\n%s: ID:%d, RET_%d!", "DEV9.IRX", dev9_id, dev9_ret);
return -7;
}
}
#endif
Expand Down
6 changes: 6 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ int getNumLines(const char *str);
void replaceIllegalChars(const char *str, char* valid, char replacement);
// Remove trailing whitespace from str.
char* rtrim(char *str);
// Get the file path portion following the ":pfs:" or ":pfsN:" marker in an
// "hdd0:partition:pfs:path" style boot path, or NULL if there is no marker.
// uLaunchELF-style launchers use ":pfs:" while OPL (through ps2sdk's
// elf-loader) produces ":pfs0:".
const char *pfsPathGetFilePart(const char *path);

// Get file extension from filename. Returns null if filename doesn't have an extension.
const char* getFileExtension(const char *filename);
// Get file path without extension.
Expand Down