mirror of
https://github.com/nitraiolo/CfgUSBLoader.git
synced 2024-12-02 16:14:15 +01:00
619 lines
11 KiB
C
619 lines
11 KiB
C
/*
|
|
* DIP plugin for Custom IOS.
|
|
*
|
|
* Copyright (C) 2008-2010 Waninkoko, WiiGator.
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "dip.h"
|
|
#include "dip_calls.h"
|
|
#include "errno.h"
|
|
#include "file.h"
|
|
#include "ioctl.h"
|
|
#include "plugin.h"
|
|
#include "syscalls.h"
|
|
#include "wbfs.h"
|
|
#include "frag.h"
|
|
#include "string.h"
|
|
|
|
/* Global config */
|
|
struct dipConfig config = { 0 };
|
|
|
|
|
|
s32 __DI_CheckOffset(u32 offset)
|
|
{
|
|
u32 offmax;
|
|
|
|
/* Check disc type */
|
|
switch (config.type) {
|
|
/* Single layer */
|
|
case DISC_DVD5:
|
|
offmax = DVD5_LENGTH;
|
|
break;
|
|
|
|
/* Dual layer */
|
|
case DISC_DVD9:
|
|
offmax = DVD9_LENGTH;
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Check offset */
|
|
if (offset >= offmax) {
|
|
/* Set error */
|
|
config.error = ERROR_BLOCK_RANGE;
|
|
|
|
/* I/O error */
|
|
return DIP_EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 __DI_ReadUnencrypted(void *outbuf, u32 len, u32 offset)
|
|
{
|
|
s32 ret;
|
|
|
|
/* Check offset */
|
|
ret = __DI_CheckOffset(offset);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Update offset */
|
|
offset += (config.offset[0] + config.offset[1]);
|
|
|
|
/* Frag read */
|
|
if (DI_ChkMode(MODE_FRAG)) {
|
|
ret = Frag_Read(outbuf, len, offset);
|
|
return ret;
|
|
}
|
|
|
|
/* File read */
|
|
if (DI_ChkMode(MODE_FILE))
|
|
return File_Read(outbuf, len, offset);
|
|
|
|
/* WBFS read */
|
|
if (DI_ChkMode(MODE_WBFS))
|
|
return WBFS_Read(outbuf, len, offset);
|
|
|
|
/* DVD read */
|
|
if (DI_ChkMode(MODE_DVDROM))
|
|
return DI_ReadDvd(outbuf, len, offset);
|
|
|
|
/* WOD read */
|
|
return DI_ReadWod(outbuf, len, offset);
|
|
}
|
|
|
|
s32 __DI_ReadDiscId(u32 *outbuf, u32 len)
|
|
{
|
|
s32 ret;
|
|
|
|
/* Read ID (first sector) */
|
|
ret = __DI_ReadUnencrypted(outbuf, len, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Check WOD magic word */
|
|
if (outbuf[6] == WOD_MAGIC) {
|
|
extern u8 *dip_readctrl;
|
|
|
|
/* Set read control */
|
|
dip_readctrl[0] = 1;
|
|
|
|
/* Read hash */
|
|
if (!dip_readctrl[1])
|
|
ret = DI_ReadHash();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void __DI_CheckDisc(void)
|
|
{
|
|
void *buffer;
|
|
s32 ret;
|
|
|
|
/* Allocate buffer */
|
|
buffer = DI_Alloc(SECTOR_SIZE, 32);
|
|
if (!buffer)
|
|
return;
|
|
|
|
/* Read second layer */
|
|
ret = __DI_ReadUnencrypted(buffer, SECTOR_SIZE, 0x50000000);
|
|
|
|
/* Set disc type */
|
|
config.type = (!ret) ? DISC_DVD9 : DISC_DVD5;
|
|
|
|
/* Free buffer */
|
|
DI_Free(buffer);
|
|
}
|
|
|
|
void __DI_ResetConfig(void)
|
|
{
|
|
/* Reset modes */
|
|
DI_DelMode(MODE_CRYPT);
|
|
DI_DelMode(MODE_DVDROM);
|
|
|
|
/* Reset offsets */
|
|
config.offset[0] = 0;
|
|
config.offset[1] = 0;
|
|
|
|
/* Reset variables */
|
|
config.type = 0;
|
|
config.error = 0;
|
|
config.cover = 0;
|
|
config.noreset = 0;
|
|
}
|
|
|
|
s32 DI_EmulateCmd(u32 *inbuf, u32 *outbuf, u32 size)
|
|
{
|
|
u32 cmd = (inbuf[0] >> 24);
|
|
|
|
s32 res;
|
|
s32 ret = 0;
|
|
|
|
/* Reset error */
|
|
if (cmd != IOCTL_DI_REQERROR)
|
|
config.error = 0;
|
|
|
|
switch(cmd) {
|
|
/** Reset drive **/
|
|
case IOCTL_DI_RESET: {
|
|
/* Check reset flag */
|
|
if (!config.noreset) {
|
|
/* Reset DIP config */
|
|
__DI_ResetConfig();
|
|
|
|
/* Non-DVD mode */
|
|
if (DI_ChkMode(MODE_EMUL)) {
|
|
/* Stop motor */
|
|
DI_StopMotor();
|
|
|
|
/* Set cover register */
|
|
BIT_SET(config.cover, 4);
|
|
} else {
|
|
/* Reset drive */
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/** Read disc ID **/
|
|
case IOCTL_DI_READID: {
|
|
u32 offset = (config.offset[0] | config.offset[1]);
|
|
|
|
/* Read disc ID */
|
|
if (!DI_ChkMode(MODE_EMUL)) {
|
|
/* Call command */
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
/* Set DVD mode */
|
|
if (ret)
|
|
DI_SetMode(MODE_DVDROM);
|
|
}
|
|
|
|
/* Manual read */
|
|
if (DI_ChkMode(MODE_DVDROM | MODE_EMUL) || offset)
|
|
ret = __DI_ReadDiscId(outbuf, size);
|
|
|
|
/* Check disc type */
|
|
if (!ret)
|
|
__DI_CheckDisc();
|
|
|
|
break;
|
|
}
|
|
|
|
/** Encrypted disc read **/
|
|
case IOCTL_DI_LOW_READ: {
|
|
/* Crypted read */
|
|
if (DI_ChkMode(MODE_CRYPT)) {
|
|
u32 len = inbuf[1];
|
|
u32 offset = inbuf[2];
|
|
|
|
/* Do unencrypted read */
|
|
ret = __DI_ReadUnencrypted(outbuf, len, offset);
|
|
} else
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Unencrypted disc read **/
|
|
case IOCTL_DI_UNENCREAD:
|
|
case IOCTL_DI_READ_A8:
|
|
case IOCTL_DI_READ_D0: {
|
|
u32 len = inbuf[1];
|
|
u32 offset = inbuf[2];
|
|
|
|
/* Change values unit */
|
|
if (cmd == IOCTL_DI_READ_D0) {
|
|
len <<= 11;
|
|
offset <<= 9;
|
|
}
|
|
|
|
/* Unencrypted read */
|
|
ret = __DI_ReadUnencrypted(outbuf, len, offset);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Disc BCA read **/
|
|
case IOCTL_DI_READBCA: {
|
|
/* Read disc BCA */
|
|
ret = __DI_ReadUnencrypted(outbuf, size, 0x40);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Set drive offset **/
|
|
case IOCTL_DI_OFFSET: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_DVDROM | MODE_EMUL);
|
|
|
|
/* Set disc offset */
|
|
if (res) {
|
|
/* Calculate offset */
|
|
u32 offset = (inbuf[1] << 30) | inbuf[2];
|
|
|
|
/* Set drive offset */
|
|
config.offset[1] = (offset & -0x8000);
|
|
} else
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Seek disc **/
|
|
case IOCTL_DI_SEEK: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_DVDROM | MODE_EMUL);
|
|
|
|
/* Seek disc */
|
|
if (!res)
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Audio config **/
|
|
case IOCTL_DI_AUDIO_CONFIG: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_DVDROM | MODE_EMUL);
|
|
|
|
/* Set audio config */
|
|
if (!res)
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Report DVD key **/
|
|
case IOCTL_DI_REPORT_KEY: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_DVDROM | MODE_EMUL);
|
|
|
|
/* Report DVD key */
|
|
if (res) {
|
|
/* Wrong disc */
|
|
config.error = ERROR_WRONG_DISC;
|
|
|
|
/* I/O error */
|
|
ret = DIP_EIO;
|
|
} else
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Request cover status **/
|
|
case IOCTL_DI_REQCOVER: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Request cover status */
|
|
if (res)
|
|
*outbuf = 0;
|
|
else
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Request error code **/
|
|
case IOCTL_DI_REQERROR: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Request error code */
|
|
if (res || config.error)
|
|
*outbuf = config.error;
|
|
else
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Set offset base **/
|
|
case IOCTL_DI_OFFSET_SET: {
|
|
u32 offset = inbuf[1];
|
|
|
|
/* Set base offset */
|
|
config.offset[0] = offset;
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get offset base **/
|
|
case IOCTL_DI_OFFSET_GET: {
|
|
/* Return offset base */
|
|
*outbuf = config.offset[0];
|
|
|
|
break;
|
|
}
|
|
|
|
/** Set crypt mode **/
|
|
case IOCTL_DI_CRYPT_SET: {
|
|
u32 mode = inbuf[1];
|
|
|
|
/* Enable crypt mode */
|
|
if (mode)
|
|
DI_SetMode(MODE_CRYPT);
|
|
else
|
|
DI_DelMode(MODE_CRYPT);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get crypt mode **/
|
|
case IOCTL_DI_CRYPT_GET: {
|
|
/* Check crypt bit */
|
|
*outbuf = DI_ChkMode(MODE_CRYPT);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Set WBFS mode **/
|
|
case IOCTL_DI_WBFS_SET: {
|
|
u32 device = inbuf[1];
|
|
|
|
/* Close WBFS */
|
|
WBFS_Close();
|
|
|
|
/* Disable mode */
|
|
DI_DelMode(MODE_WBFS);
|
|
|
|
/* Check device */
|
|
if (device) {
|
|
u8 *discid = (u8 *)&inbuf[2];
|
|
|
|
/* Open device */
|
|
ret = WBFS_Open(device-1, discid);
|
|
|
|
/* Enable mode */
|
|
if (!ret)
|
|
DI_SetMode(MODE_WBFS);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get WBFS mode **/
|
|
case IOCTL_DI_WBFS_GET: {
|
|
/* Check WBFS bit */
|
|
*outbuf = DI_ChkMode(MODE_WBFS);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Set file mode **/
|
|
case IOCTL_DI_FILE_SET: {
|
|
char *filename = (char *)inbuf[1];
|
|
|
|
/* Close file */
|
|
File_Close();
|
|
|
|
/* Disable mode */
|
|
DI_DelMode(MODE_FILE);
|
|
|
|
/* Check flag */
|
|
if (filename) {
|
|
/* Convert address */
|
|
filename = VirtToPhys(filename);
|
|
|
|
/* Open file */
|
|
ret = File_Open(filename);
|
|
|
|
/* Enable mode */
|
|
if (!ret)
|
|
DI_SetMode(MODE_FILE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get file mode **/
|
|
case IOCTL_DI_FILE_GET: {
|
|
/* Check file bit */
|
|
*outbuf = DI_ChkMode(MODE_FILE);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Set FRAG mode **/
|
|
case IOCTL_DI_FRAG_SET: {
|
|
u32 device = inbuf[1];
|
|
void *fraglist = (void*)inbuf[2];
|
|
int size = inbuf[3];
|
|
|
|
/* Close frag */
|
|
Frag_Close();
|
|
|
|
/* Disable mode */
|
|
DI_DelMode(MODE_FRAG);
|
|
|
|
/* Check device */
|
|
if (device && fraglist && size) {
|
|
/* Convert address */
|
|
fraglist = VirtToPhys(fraglist);
|
|
|
|
/* Open device */
|
|
ret = Frag_Init(device, fraglist, size);
|
|
*outbuf = ret;
|
|
|
|
/* Enable mode */
|
|
if (ret > 0)
|
|
DI_SetMode(MODE_FRAG);
|
|
|
|
ret = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/** Get IO mode **/
|
|
case IOCTL_DI_MODE_GET: {
|
|
/* return all mode bits */
|
|
*outbuf = config.mode;
|
|
break;
|
|
}
|
|
|
|
/** debug stuff **/
|
|
case IOCTL_DI_HELLO: {
|
|
memcpy(outbuf,"HELO",4);
|
|
outbuf[1] = config.mode;
|
|
outbuf[2] = config.type;
|
|
break;
|
|
}
|
|
|
|
/** Disable reset **/
|
|
case IOCTL_DI_RESET_DISABLE: {
|
|
u32 value = inbuf[1];
|
|
|
|
/* Disable reset */
|
|
config.noreset = value;
|
|
|
|
break;
|
|
}
|
|
|
|
/** Send custom DVD command **/
|
|
case IOCTL_DI_CUSTOMCMD: {
|
|
void *buffer = (void *)inbuf[1];
|
|
|
|
/* Convert address to physical */
|
|
buffer = VirtToPhys(buffer);
|
|
|
|
/* Send custom DI command */
|
|
ret = DI_CustomCmd(buffer, outbuf);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* Call command */
|
|
ret = DI_HandleCmd(inbuf, outbuf, size);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
s32 DI_EmulateIoctl(ioctl *buffer, s32 fd)
|
|
{
|
|
u32 *outbuf = buffer->iobuf;
|
|
u32 cmd = buffer->command;
|
|
|
|
s32 res;
|
|
s32 ret = 1;
|
|
|
|
/* Parse command */
|
|
switch (cmd) {
|
|
/** Wait for cover close **/
|
|
case IOCTL_DI_WAITCVRCLOSE: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Wait for cover close */
|
|
if (!res)
|
|
ret = DI_HandleIoctl(buffer, fd);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get cover register **/
|
|
case IOCTL_DI_COVER_REG: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Get cover register */
|
|
if (res)
|
|
*outbuf = config.cover;
|
|
else
|
|
ret = DI_HandleIoctl(buffer, fd);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Clear cover interrupt **/
|
|
case IOCTL_DI_COVER_CLEAR: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Clear cover interrupt */
|
|
if (res)
|
|
BIT_DEL(config.cover, 4);
|
|
else
|
|
ret = DI_HandleIoctl(buffer, fd);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get cover status **/
|
|
case IOCTL_DI_COVER_STATUS: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Get cover status */
|
|
if (res)
|
|
*outbuf = 0x02;
|
|
else
|
|
ret = DI_HandleIoctl(buffer, fd);
|
|
|
|
break;
|
|
}
|
|
|
|
/** Get status register **/
|
|
case IOCTL_DI_STATUS_REG: {
|
|
/* Check modes */
|
|
res = DI_ChkMode(MODE_EMUL);
|
|
|
|
/* Get status register */
|
|
if (res)
|
|
*outbuf = 0x0A;
|
|
else
|
|
ret = DI_HandleIoctl(buffer, fd);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* Call IOCTL */
|
|
ret = DI_HandleIoctl(buffer, fd);
|
|
}
|
|
|
|
return ret;
|
|
}
|