/** * @file io_funcs.c * @author Karim Vergnes * @copyright GPLv2 * @brief Functions to interpose with UMD driver * * Functions to parse and manipulate devctl requests to the UMD driver, * allowing for live redirection of read requests to a patch file. */ #include "io_funcs.h" #include extern PspIoDrvFuncs reserveUmdFuncs; static char umd_id[11] = {0}; static char first_read = 1; static char hdr[ISO_SECTOR_SIZE]; /** * @brief Convert layout-based address (LBA) to an absolute offset. * * @param param The LBA parameter data, as obtained from a read devctl. */ static inline u32 _impl_lp_UmdLba_toAddr(lp_UmdLba *param) { u32 offset; if (!param->byte_size_start) { offset = param->lba_top * ISO_SECTOR_SIZE; } else if (param->byte_size_centre) { offset = param->lba_top * ISO_SECTOR_SIZE - param->byte_size_start + ISO_SECTOR_SIZE; } else if (!param->byte_size_last) { offset = param->lba_top * ISO_SECTOR_SIZE + param->byte_size_start; } else { offset = param->lba_top * ISO_SECTOR_SIZE - param->byte_size_start + ISO_SECTOR_SIZE; } return offset; } /** * @brief Read the header sector for the disc to memory. * * This function is meant to be read in response to an existing read devctl. * The parameters below should be patched through from a previous devctl call. * * @param arg The devctl file argument to be passed through to the driver. * @param devname The device name argument to be passed through to the driver. */ static inline int _impl_lp_readDiscHeader(PspIoDrvFileArg *arg, const char *devname) { int ret; lp_UmdLba param = { .unknown1 = 0, .cmd = 0, /* read */ .lba_top = 0x8000 / ISO_SECTOR_SIZE, .lba_size = 1, .byte_size_total = ISO_SECTOR_SIZE, .byte_size_centre = ISO_SECTOR_SIZE, .byte_size_start = 0, .byte_size_last = 0 }; ret = reserveUmdFuncs.IoDevctl(arg, devname, lp_UmdDevctl_READ_SECTORS, ¶m, sizeof param, &hdr, ISO_SECTOR_SIZE); return ret; } /** * @brief The effective low-level read command. * * This function is called whenever a devctl to read data off disc is * intercepted. All parameters are provided from the {@ref PspIoDrvFuncs::IoDevctl} * function call made by the caller, to allow seamless passthrough to the * original driver. * * @param param The parsed {@ref lp_UmdLba} layout-based address. * @param outdata The memory address where read data is expected. * @param outlen The amount of data expected to be read. * * @see _impl_lp_lbaToAddr to convert the LBA into an absolute bytes offset. */ static int _impl_lp_devctlRead(PspIoDrvFileArg *arg, const char *devname, unsigned int cmd, lp_UmdLba *param, int inlen, void *outdata, int outlen) { u32 offset = _impl_lp_UmdLba_toAddr(param); int ret; if (first_read) { ret = reserveUmdFuncs.IoDevctl(arg, devname, cmd, param, inlen, outdata, outlen); _impl_lp_readDiscHeader(arg, devname); strncpy(umd_id, hdr + 0x373, 10); umd_id[10] = 0; Kprintf("Disc ID obtained: '%s'\n", umd_id); first_read = 0; return ret; } passthru: return reserveUmdFuncs.IoDevctl(arg, devname, cmd, param, inlen, outdata, outlen); } int patched_IoDevctl(PspIoDrvFileArg *arg, const char *devname, unsigned int cmd, void *indata, int inlen, void *outdata, int outlen) { switch ((lp_UmdDevctl) cmd) { case lp_UmdDevctl_READ_GENERAL: case lp_UmdDevctl_READ_CACHE: case lp_UmdDevctl_READ_SECTORS: return _impl_lp_devctlRead(arg, devname, cmd, (lp_UmdLba *) indata, inlen, outdata, outlen); default: goto passthru; } passthru: return reserveUmdFuncs.IoDevctl(arg, devname, cmd, indata, inlen, outdata, outlen); } int patched_IoRead(PspIoDrvFileArg *arg, char *data, int len) { Kprintf("Reading UMD data, hum dee dum...\n"); return reserveUmdFuncs.IoRead(arg, data, len); } int patched_IoOpen(PspIoDrvFileArg *arg, char *file, int flags, SceMode mode) { Kprintf("Opening UMD.\n"); return reserveUmdFuncs.IoOpen(arg, file, flags, mode); } void lp_pingDiscRemoved(void) { first_read = 1; } // vim: ft=c.doxygen