From 353721578d0ec7b49a4d7b56b0641acce8d8a52f Mon Sep 17 00:00:00 2001 From: TakaRikka <38417346+TakaRikka@users.noreply.github.com> Date: Mon, 8 Dec 2025 02:44:29 -0800 Subject: [PATCH] shieldD revolution dvd/nand mostly done (#2922) * shieldD revo dvd mostly done * shieldD revo nand mostly done * shieldD revo fs mostly done * shieldD revo ipc mostly done * shieldD revo sdk pad done --- config/ShieldD/splits.txt | 8 +- configure.py | 47 + include/revolution/dvd.h | 111 +- include/revolution/fs.h | 100 + include/revolution/ipc.h | 24 + include/revolution/ipc/ipcProfile.h | 19 + include/revolution/ipc/ipcclt.h | 37 + include/revolution/ipc/memory.h | 18 + include/revolution/nand.h | 89 +- include/revolution/os.h | 20 +- include/revolution/os/OSNet.h | 29 + include/revolution/os/OSPlayRecord.h | 8 + include/revolution/os/OSReset.h | 4 +- include/revolution/private/iosresclt.h | 9 - include/revolution/private/iosrestypes.h | 14 +- src/revolution/dvd/__dvd.h | 34 +- src/revolution/dvd/dvd.c | 2553 ++++++++++++++++++++++ src/revolution/dvd/dvdDeviceError.c | 200 ++ src/revolution/dvd/dvdFatal.c | 211 ++ src/revolution/dvd/dvd_broadway.c | 978 +++++++++ src/revolution/dvd/dvderror.c | 202 ++ src/revolution/dvd/dvdfs.c | 557 +++++ src/revolution/dvd/dvdidutils.c | 98 + src/revolution/dvd/dvdqueue.c | 192 ++ src/revolution/fs/fs.c | 973 +++++++++ src/revolution/ipc/ipcMain.c | 51 + src/revolution/ipc/ipcProfile.c | 53 + src/revolution/ipc/ipcclt.c | 835 +++++++ src/revolution/ipc/memory.c | 240 ++ src/revolution/nand/NANDCheck.c | 94 + src/revolution/nand/NANDCore.c | 514 +++++ src/revolution/nand/NANDErrorMessage.c | 235 ++ src/revolution/nand/NANDLogging.c | 181 ++ src/revolution/nand/NANDOpenClose.c | 683 ++++++ src/revolution/nand/nand.c | 565 +++++ src/revolution/os/OS.c | 7 +- src/revolution/os/OSAlarm.c | 4 +- src/revolution/os/OSAudioSystem.c | 2 + src/revolution/os/OSExec.c | 3 +- src/revolution/os/OSInterrupt.c | 1 + src/revolution/os/OSLaunch.c | 1 + src/revolution/os/OSLink.c | 1 + src/revolution/os/OSNet.c | 19 +- src/revolution/os/OSPlayTime.c | 4 + src/revolution/os/OSReset.c | 2 + src/revolution/os/OSStateFlags.c | 2 +- src/revolution/os/OSStateTM.c | 2 + src/revolution/os/OSSync.c | 1 + src/revolution/os/OSThread.c | 6 +- src/revolution/os/__os.h | 47 +- src/revolution/pad/Pad.c | 894 ++++++++ src/revolution/pad/Padclamp.c | 153 ++ src/revolution/si/__si.h | 21 + 53 files changed, 11021 insertions(+), 135 deletions(-) create mode 100644 include/revolution/fs.h create mode 100644 include/revolution/ipc.h create mode 100644 include/revolution/ipc/ipcProfile.h create mode 100644 include/revolution/ipc/ipcclt.h create mode 100644 include/revolution/ipc/memory.h create mode 100644 include/revolution/os/OSNet.h delete mode 100644 include/revolution/private/iosresclt.h create mode 100644 src/revolution/dvd/dvd.c create mode 100644 src/revolution/dvd/dvdDeviceError.c create mode 100644 src/revolution/dvd/dvdFatal.c create mode 100644 src/revolution/dvd/dvd_broadway.c create mode 100644 src/revolution/dvd/dvderror.c create mode 100644 src/revolution/dvd/dvdfs.c create mode 100644 src/revolution/dvd/dvdidutils.c create mode 100644 src/revolution/dvd/dvdqueue.c create mode 100644 src/revolution/fs/fs.c create mode 100644 src/revolution/ipc/ipcMain.c create mode 100644 src/revolution/ipc/ipcProfile.c create mode 100644 src/revolution/ipc/ipcclt.c create mode 100644 src/revolution/ipc/memory.c create mode 100644 src/revolution/nand/NANDCheck.c create mode 100644 src/revolution/nand/NANDCore.c create mode 100644 src/revolution/nand/NANDErrorMessage.c create mode 100644 src/revolution/nand/NANDLogging.c create mode 100644 src/revolution/nand/NANDOpenClose.c create mode 100644 src/revolution/nand/nand.c create mode 100644 src/revolution/pad/Pad.c create mode 100644 src/revolution/pad/Padclamp.c create mode 100644 src/revolution/si/__si.h diff --git a/config/ShieldD/splits.txt b/config/ShieldD/splits.txt index e2980dc623..2139785b55 100644 --- a/config/ShieldD/splits.txt +++ b/config/ShieldD/splits.txt @@ -4330,7 +4330,7 @@ revolution/dvd/dvdDeviceError.c: .rodata start:0x8065EA50 end:0x8065EA70 .data start:0x8073A040 end:0x8073A1D8 .sdata start:0x8074BFE0 end:0x8074BFE8 - .sbss start:0x8074D530 end:0x8074D53C + .sbss start:0x8074D530 end:0x8074D538 .sdata2 start:0x80752E30 end:0x80752E38 .bss start:0x80801880 end:0x808018A0 @@ -4338,7 +4338,7 @@ revolution/dvd/dvd_broadway.c: .text start:0x80607A70 end:0x80609A20 .data start:0x8073A1D8 end:0x8073B118 .sdata start:0x8074BFE8 end:0x8074BFF8 - .sbss start:0x8074D53C end:0x8074D560 + .sbss start:0x8074D538 end:0x8074D560 .bss start:0x808018A0 end:0x80801A20 revolution/ai/ai.c: @@ -4412,7 +4412,7 @@ revolution/sc/scsystem.c: .rodata start:0x8065EE18 end:0x8065EE70 .data start:0x8073DB08 end:0x8073DDB0 .sdata start:0x8074C078 end:0x8074C1C0 - .sbss start:0x8074D5EC end:0x8074D604 + .sbss start:0x8074D5EC end:0x8074D600 .bss start:0x80801DC0 end:0x80809F60 revolution/sc/scapi.c: @@ -4435,7 +4435,7 @@ revolution/esp/esp.c: revolution/ipc/ipcMain.c: .text start:0x80612240 end:0x806123B0 .data start:0x8073DE10 end:0x8073DE48 - .sbss start:0x8074D604 end:0x8074D618 + .sbss start:0x8074D600 end:0x8074D618 revolution/ipc/ipcclt.c: .text start:0x806123B0 end:0x80613CC0 diff --git a/configure.py b/configure.py index 554798a8d7..d00d071b28 100755 --- a/configure.py +++ b/configure.py @@ -397,6 +397,7 @@ cflags_revolution_retail = [ cflags_revolution_debug = [ *cflags_revolution_base, "-opt off", + "-inline off", "-DDEBUG=1", ] @@ -1497,6 +1498,52 @@ config.libs = [ Object(MatchingFor("ShieldD"), "revolution/os/__ppc_eabi_init.cpp"), ], ), + RevolutionLib( + "dvd", + [ + Object(NonMatching, "revolution/dvd/dvdfs.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvd.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvdqueue.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvderror.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvdidutils.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvdFatal.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvdDeviceError.c", extra_cflags=["-char signed"]), + Object(NonMatching, "revolution/dvd/dvd_broadway.c", extra_cflags=["-char signed"]), + ], + ), + RevolutionLib( + "nand", + [ + Object(NonMatching, "revolution/nand/nand.c"), + Object(NonMatching, "revolution/nand/NANDOpenClose.c"), + Object(NonMatching, "revolution/nand/NANDCore.c"), + Object(NonMatching, "revolution/nand/NANDCheck.c"), + Object(NonMatching, "revolution/nand/NANDLogging.c"), + Object(NonMatching, "revolution/nand/NANDErrorMessage.c"), + ], + ), + RevolutionLib( + "fs", + [ + Object(NonMatching, "revolution/fs/fs.c"), + ], + ), + RevolutionLib( + "ipc", + [ + Object(NonMatching, "revolution/ipc/ipcMain.c"), + Object(NonMatching, "revolution/ipc/ipcclt.c"), + Object(NonMatching, "revolution/ipc/memory.c"), + Object(NonMatching, "revolution/ipc/ipcProfile.c"), + ], + ), + RevolutionLib( + "pad", + [ + Object(NonMatching, "revolution/pad/Padclamp.c"), + Object(NonMatching, "revolution/pad/Pad.c"), + ], + ), { "lib": "Runtime.PPCEABI.H", "mw_version": MWVersion(config.version), diff --git a/include/revolution/dvd.h b/include/revolution/dvd.h index 3ab240ec37..5dc9067cd8 100644 --- a/include/revolution/dvd.h +++ b/include/revolution/dvd.h @@ -66,6 +66,8 @@ extern "C" { #define DVD_COMMAND_BS_CHANGE_DISK 15 #define DVD_COMMAND_UNK_16 16 +#define DVD_RESETCOVER_TIMELAG_TICKS2 OSMillisecondsToTicks(100) + typedef struct DVDDiskID { char gameName[4]; char company[2]; @@ -137,6 +139,29 @@ typedef struct DVDDriveInfo { /* 0x08 */ u8 padding[24]; } DVDDriveInfo; +typedef struct DVDCommandInfo DVDCommandInfo; +struct DVDCommandInfo { + u32 command; + u32 offset; + u32 length; + u32 intType; + u32 tick; +}; + +typedef struct DVDErrorInfo DVDErrorInfo; +struct DVDErrorInfo { + char gameName[4]; + u8 diskNumber; + u8 gameVersion; + u8 reserved0[2]; + u32 error; + u32 dateTime; + u32 status; + u32 unk_0x14; + u32 nextOffset; + DVDCommandInfo lastCommand[5]; +}; + typedef struct DVDGamePartition { ESTicket ticket; u32 tmdSize; @@ -157,25 +182,50 @@ typedef struct DVDGameTOC { DVDPartitionInfo* partitionInfos; } DVDGameTOC; +#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1)) + +typedef struct DVDPartitionParams DVDPartitionParams; + +struct DVDPartitionParams { + ESTicket ticket; + u8 padding0[ROUND(sizeof(ESTicket), 32) - sizeof(ESTicket)]; + ESTicketView ticketView; + u8 padding1[ROUND(sizeof(ESTicketView), 32) - sizeof(ESTicketView)]; + u32 numTmdBytes; + u8 padding2[28]; + ESTitleMeta tmd; + u8 padding3[ROUND(sizeof(ESTitleMeta), 32) - sizeof(ESTitleMeta)]; + u32 numCertBytes; + u8 padding4[28]; + u8 certificates[4096]; + u32 dataWordOffset; + u8 padding5[28]; + u8 h3Hash[98304]; +}; + +typedef struct diRegVals { + u32 ImmRegVal; + u32 CoverRegVal; + u32 pad[6]; +} diRegVals_t; + +typedef struct diCommand { + u8 theCommand; + u8 pad1[3]; + u32 arg[5]; + u32 pad2[2]; +} diCommand_t; + +typedef struct DVDVideoReportKey { + u8 data[32]; +} DVDVideoReportKey; + // DVD void DVDInit(void); int DVDReadAbsAsyncPrio(DVDCommandBlock* block, void* addr, s32 length, s32 offset, DVDCBCallback callback, s32 prio); int DVDSeekAbsAsyncPrio(DVDCommandBlock* block, s32 offset, DVDCBCallback callback, s32 prio); int DVDReadAbsAsyncForBS(DVDCommandBlock* block, void* addr, s32 length, s32 offset, DVDCBCallback callback); int DVDReadDiskID(DVDCommandBlock* block, DVDDiskID* diskID, DVDCBCallback callback); -int DVDPrepareStreamAbsAsync(DVDCommandBlock* block, u32 length, u32 offset, DVDCBCallback callback); -int DVDCancelStreamAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDCancelStream(DVDCommandBlock* block); -int DVDStopStreamAtEndAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDStopStreamAtEnd(DVDCommandBlock* block); -int DVDGetStreamErrorStatusAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDGetStreamErrorStatus(DVDCommandBlock* block); -int DVDGetStreamPlayAddrAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDGetStreamPlayAddr(DVDCommandBlock* block); -int DVDGetStreamStartAddrAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDGetStreamStartAddr(DVDCommandBlock* block); -int DVDGetStreamLengthAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDGetStreamLength(DVDCommandBlock* block); int DVDChangeDiskAsyncForBS(DVDCommandBlock* block, DVDCBCallback callback); int DVDChangeDiskAsync(DVDCommandBlock* block, DVDDiskID* id, DVDCBCallback callback); s32 DVDChangeDisk(DVDCommandBlock* block, DVDDiskID* id); @@ -191,7 +241,7 @@ BOOL DVDSetAutoInvalidation(BOOL autoInval); void DVDPause(void); void DVDResume(void); int DVDCancelAsync(DVDCommandBlock* block, DVDCBCallback callback); -s32 DVDCancel(volatile DVDCommandBlock* block); +s32 DVDCancel(DVDCommandBlock* block); int DVDCancelAllAsync(DVDCBCallback callback); s32 DVDCancelAll(void); DVDDiskID* DVDGetCurrentDiskID(void); @@ -199,6 +249,7 @@ BOOL DVDCheckDisk(void); // DVD FATAL int DVDSetAutoFatalMessaging(BOOL enable); +BOOL __DVDGetAutoFatalMessaging(void); // DVD FS s32 DVDConvertPathToEntrynum(const char* pathPtr); @@ -218,9 +269,6 @@ int DVDOpenDir(const char* dirName, DVDDir* dir); int DVDReadDir(DVDDir* dir, DVDDirEntry* dirent); int DVDCloseDir(DVDDir* dir); void DVDRewindDir(DVDDir* dir); -void* DVDGetFSTLocation(void); -BOOL DVDPrepareStreamAsync(DVDFileInfo* fileInfo, u32 length, u32 offset, DVDCallback callback); -s32 DVDPrepareStream(DVDFileInfo* fileInfo, u32 length, u32 offset); s32 DVDGetTransferredSize(DVDFileInfo* fileinfo); #define DVDReadAsync(fileInfo, addr, length, offset, callback) \ @@ -233,16 +281,11 @@ DVDDiskID* DVDGenerateDiskID(DVDDiskID* id, const char* game, const char* compan // DVD LOW BOOL DVDLowRead(void* addr, u32 length, u32 offset, DVDLowCallback callback); BOOL DVDLowSeek(u32 offset, DVDLowCallback callback); -BOOL DVDLowWaitCoverClose(DVDLowCallback callback); BOOL DVDLowReadDiskID(DVDDiskID* diskID, DVDLowCallback callback); -BOOL DVDLowStopMotor(DVDLowCallback callback); BOOL DVDLowRequestError(DVDLowCallback callback); BOOL DVDLowInquiry(DVDDriveInfo* info, DVDLowCallback callback); -BOOL DVDLowAudioStream(u32 subcmd, u32 length, u32 offset, DVDLowCallback callback); -BOOL DVDLowRequestAudioStatus(u32 subcmd, DVDLowCallback callback); -BOOL DVDLowAudioBufferConfig(BOOL enable, u32 size, DVDLowCallback callback); -void DVDLowReset(void); -DVDLowCallback DVDLowSetResetCoverCallback(DVDLowCallback callback); +BOOL DVDLowAudioBufferConfig(u8 enable, u32 size, DVDLowCallback callback); +BOOL DVDLowReset(DVDLowCallback callback); BOOL DVDLowBreak(void); DVDLowCallback DVDLowClearCallback(void); u32 DVDLowGetCoverStatus(void); @@ -250,8 +293,24 @@ u32 DVDLowGetCoverStatus(void); // DVD QUEUE void DVDDumpWaitingQueue(void); - -// unsorted revo +// DVD BROADWAY +BOOL DVDLowFinalize(void); +BOOL DVDLowInit(void); +BOOL DVDLowUnmaskStatusInterrupts(void); +BOOL DVDLowMaskCoverInterrupt(void); +BOOL DVDLowClearCoverInterrupt(DVDLowCallback callback); +BOOL DVDLowSetSpinupFlag(u32 spinUp); +u32 DVDLowGetImmBufferReg(void); +BOOL DVDLowSetMaximumRotation(u32 subcmd, DVDLowCallback callback); +u32 DVDLowGetCoverRegister(void); +BOOL DVDLowPrepareCoverRegister(DVDLowCallback callback); +BOOL DVDLowGetNoDiscBufferSizes(const u32 partitionWordOffset, u32* numTmdBytes, u32* numCertBytes, DVDLowCallback callback); +BOOL DVDLowGetNoDiscOpenPartitionParams(const u32 partitionWordOffset, ESTicket* eTicket, u32* numTmdBytes, ESTitleMeta* tmd, u32* numCertBytes, u8* certificates, u32* dataWordOffset, u8* h3HashPtr, DVDLowCallback callback); +BOOL DVDLowReportKey(DVDVideoReportKey* reportKey, u32 format, u32 lsn, DVDLowCallback callback); +u32 DVDLowGetControlRegister(void); +u32 DVDLowGetStatusRegister(void); +BOOL DVDLowPrepareControlRegister(DVDLowCallback callback); +BOOL DVDLowPrepareStatusRegister(DVDLowCallback callback); BOOL DVDLowUnencryptedRead(void*, u32, u32, DVDLowCallback); BOOL DVDLowClosePartition(DVDLowCallback); BOOL DVDLowOpenPartitionWithTmdAndTicketView(const u32, const ESTicketView* const, const u32, const ESTitleMeta* const, const u32, const u8* const, DVDLowCallback); diff --git a/include/revolution/fs.h b/include/revolution/fs.h new file mode 100644 index 0000000000..b6019c9743 --- /dev/null +++ b/include/revolution/fs.h @@ -0,0 +1,100 @@ +#ifndef _REVOLUTION_FS_H_ +#define _REVOLUTION_FS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISFS_ERROR_OK 0 +#define ISFS_ERROR_INVALID -101 +#define ISFS_ERROR_ACCESS -102 +#define ISFS_ERROR_CORRUPT -103 +#define ISFS_ERROR_NOTREADY -104 +#define ISFS_ERROR_EXISTS -105 +#define ISFS_ERROR_NOEXISTS -106 +#define ISFS_ERROR_MAXFILES -107 +#define ISFS_ERROR_MAXBLOCKS -108 +#define ISFS_ERROR_MAXFD -109 +#define ISFS_ERROR_MAXDEPTH -110 +#define ISFS_ERROR_OPENFD -111 +#define ISFS_ERROR_BADBLOCK -112 +#define ISFS_ERROR_ECC -113 +#define ISFS_ERROR_ECC_CRIT -114 +#define ISFS_ERROR_NOTEMPTY -115 +#define ISFS_ERROR_HMAC -116 +#define ISFS_ERROR_UNKNOWN -117 +#define ISFS_ERROR_BUSY -118 +#define ISFS_ERROR_SHUTDOWN -119 + +#define ISFS_INODE_NAMELEN 12 + +typedef s32 ISFSError; +typedef void (*ISFSCallback) (ISFSError, void *ctxt); + +typedef struct { + u32 blockSize; + u32 freeBlocks; + u32 occupiedBlcocks; + u32 badBlocks; + u32 reservedBlocks; + u32 freeInodes; + u32 occupedInodes; +} ISFSStats; + +typedef struct { + u32 size; + u32 offset; +} ISFSFileStats; + +typedef struct { + IOSUid ownerId; + IOSGid groupId; + u8 path[64]; + u8 ownerAccess; + u8 groupAccess; + u8 othersAccess; + u8 attr; +} ISFSPathAttrArgs; + +typedef struct { + u8 path1[64]; + u8 path2[64]; +} ISFSPathsArgs; + +ISFSError ISFS_OpenLib(void); +s32 ISFS_CreateDir(const u8* dname, u32 dirAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc); +s32 ISFS_CreateDirAsync(const u8* dname, u32 dirAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc, ISFSCallback cb, void* fsCtxt); +s32 ISFS_ReadDir(const u8* dname, u8* nameList, u32* num); +s32 ISFS_ReadDirAsync(const u8* dname, u8* nameList, u32* num, ISFSCallback cb, void* fsCtxt); +s32 ISFS_GetAttr(const u8* name, IOSUid* ownerId, IOSGid* groupId, u32* attr, u32* ownerAcc, u32* groupAcc, u32* othersAcc); +s32 ISFS_GetAttrAsync(const u8* name, IOSUid* ownerId, IOSGid* groupId, u32* attr, u32* ownerAcc, u32* groupAcc, u32* othersAcc, ISFSCallback cb, void* fsCtxt); +s32 ISFS_Delete(const u8* name); +s32 ISFS_DeleteAsync(const u8* name, ISFSCallback cb, void* fsCtxt); +s32 ISFS_Rename(const u8* oldName, const u8* newName); +s32 ISFS_RenameAsync(const u8* oldName, const u8* newName, ISFSCallback cb, void* fsCtxt); +s32 ISFS_GetUsage(const u8* dname, u32* nblocks, u32* ninodes); +s32 ISFS_CreateFile(const u8* fname, u32 fileAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc); +s32 ISFS_CreateFileAsync(const u8* fname, u32 fileAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc, ISFSCallback cb, void* fsCtxt); +IOSFd ISFS_Open(const u8* fname, u32 access); +IOSFd ISFS_OpenAsync(const u8* fname, u32 access, ISFSCallback cb, void* fsCtxt); +s32 ISFS_GetFileStats(IOSFd fd, ISFSFileStats* stats); +s32 ISFS_GetFileStatsAsync(IOSFd fd, ISFSFileStats* stats, ISFSCallback cb, void* fsCtxt); +s32 ISFS_Seek(IOSFd fd, s32 offset, u32 whence); +s32 ISFS_SeekAsync(IOSFd fd, s32 offset, u32 whence, ISFSCallback cb, void* fsCtxt); +s32 ISFS_Read(s32 fd, u8* pBuffer, u32 bufSize); +s32 ISFS_ReadAsync(IOSFd fd, u8* buf, u32 size, ISFSCallback cb, void* fsCtxt); +s32 ISFS_Write(IOSFd fd, const u8* buf, u32 size); +s32 ISFS_WriteAsync(IOSFd fd, const u8* buf, u32 size, ISFSCallback cb, void* fsCtxt); +s32 ISFS_Close(IOSFd fd); +s32 ISFS_CloseAsync(IOSFd fd, ISFSCallback cb, void* fsCtxt); +s32 ISFS_ShutdownAsync(ISFSCallback cb, void* fsCtxt); + +#ifdef __cplusplus +} +#endif + +#endif // _REVOLUTION_FS_H_ diff --git a/include/revolution/ipc.h b/include/revolution/ipc.h new file mode 100644 index 0000000000..6c2ddf798d --- /dev/null +++ b/include/revolution/ipc.h @@ -0,0 +1,24 @@ +#ifndef _REVOLUTION_IPC_H_ +#define _REVOLUTION_IPC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void IPCInit(void); +void IPCReInit(void); +u32 IPCReadReg(u32 regIdx); +void IPCWriteReg(u32 regIdx, u32 data); +void* IPCGetBufferHi(void); +void* IPCGetBufferLo(void); +void IPCSetBufferLo(void* newLo); + +#ifdef __cplusplus +} +#endif + +#endif // _REVOLUTION_IPC_H_ diff --git a/include/revolution/ipc/ipcProfile.h b/include/revolution/ipc/ipcProfile.h new file mode 100644 index 0000000000..75e851bc41 --- /dev/null +++ b/include/revolution/ipc/ipcProfile.h @@ -0,0 +1,19 @@ +#ifndef _REVOLUTION_IPCPROFILE_H_ +#define _REVOLUTION_IPCPROFILE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void IPCiProfInit(void); +void IPCiProfQueueReq(void* req, s32 handle); +void IPCiProfReply(void* req, s32 handle); +void IPCiProfAck(void); + +#ifdef __cplusplus +} +#endif + +#endif // _REVOLUTION_IPCPROFILE_H_ diff --git a/include/revolution/ipc/ipcclt.h b/include/revolution/ipc/ipcclt.h new file mode 100644 index 0000000000..673cfcd688 --- /dev/null +++ b/include/revolution/ipc/ipcclt.h @@ -0,0 +1,37 @@ +#ifndef _REVOLUTION_IPCCLT_H_ +#define _REVOLUTION_IPCCLT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef IOSError (*IOSIpcCb)(IOSError, void*); + +IOSError IPCCltInit(void); +IOSError IPCCltReInit(void); + +IOSError IOS_OpenAsync(const char* pPath, u32 flags, IOSIpcCb cb, void* callback_arg); +IOSError IOS_Open(const char* path, u32 flags); +IOSError IOS_CloseAsync(IOSFd fd, IOSIpcCb cb, void* cbArg); +IOSError IOS_Close(IOSFd fd); +IOSError IOS_ReadAsync(IOSFd fd, void* buf, u32 len, IOSIpcCb cb, void* cbArg); +IOSError IOS_Read(IOSFd fd, void* buf, u32 len); +IOSError IOS_WriteAsync(IOSFd fd, void* buf, u32 len, IOSIpcCb cb, void* cbArg); +IOSError IOS_Write(IOSFd fd, void* buf, u32 len); +IOSError IOS_SeekAsync(IOSFd fd, s32 offset, u32 whence, IOSIpcCb cb, void* cbArg); +IOSError IOS_Seek(IOSFd fd, s32 offset, u32 whence); +IOSError IOS_IoctlAsync(IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen, IOSIpcCb cb, void* cbArg); +IOSError IOS_Ioctl(IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen); +IOSError IOS_IoctlvAsync(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect, IOSIpcCb cb, void* cbArg); +IOSError IOS_Ioctlv(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect); +IOSError IOS_IoctlvReboot(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect); + +#ifdef __cplusplus +} +#endif + +#endif // _REVOLUTION_IPCCLT_H_ diff --git a/include/revolution/ipc/memory.h b/include/revolution/ipc/memory.h new file mode 100644 index 0000000000..8bf1e35505 --- /dev/null +++ b/include/revolution/ipc/memory.h @@ -0,0 +1,18 @@ +#ifndef _REVOLUTION_IPCMEMORY_H_ +#define _REVOLUTION_IPCMEMORY_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +IOSError iosFree(IOSHeapId id, void* ptr); +void* iosAllocAligned(IOSHeapId id, u32 size, u32 alignment); +IOSHeapId iosCreateHeap(void* ptr, u32 size); + +#ifdef __cplusplus +} +#endif + +#endif // _REVOLUTION_IPCMEMORY_H_ diff --git a/include/revolution/nand.h b/include/revolution/nand.h index 0f095e14ab..cafe72b054 100644 --- a/include/revolution/nand.h +++ b/include/revolution/nand.h @@ -2,6 +2,7 @@ #define _REVOLUTION_NAND_H_ #include +#include #ifdef __cplusplus extern "C" { @@ -122,11 +123,56 @@ typedef struct { typedef void (*NANDCallback)(s32, NANDCommandBlock*); typedef void (*NANDAsyncCallback)(s32 result, struct NANDCommandBlock* block); +typedef void (*NANDLoggingCallback)(BOOL, s32); + +// NAND +s32 NANDCreate(const char* path, const u8 perm, const u8 attr); +s32 NANDPrivateCreate(const char* path, u8 perm, u8 attr); +s32 NANDPrivateCreateAsync(const char *path, u8 perm, u8 attr, NANDCallback cb, NANDCommandBlock* block); + +s32 NANDDelete(const char* path); +s32 NANDPrivateDelete(const char* path); +s32 NANDPrivateDeleteAsync(const char* path, NANDCallback cb, NANDCommandBlock* block); + +s32 NANDRead(NANDFileInfo* info, void* buf, const u32 length); +s32 NANDReadAsync(NANDFileInfo* info, void* buf, const u32 length, + NANDCallback cb, NANDCommandBlock* block); + +s32 NANDWrite(NANDFileInfo* info, const void* buf, const u32 length); +s32 NANDWriteAsync(NANDFileInfo* info, const void* buf, const u32 length, + NANDCallback cb, NANDCommandBlock* block); + +s32 NANDSeek(NANDFileInfo* info, const s32 offset, const s32 whence); +s32 NANDSeekAsync(NANDFileInfo* info, const s32 offset, const s32 whence, + NANDCallback cb, NANDCommandBlock* block); + +s32 NANDPrivateCreateDir(const char* path, u8 perm, u8 attr); +s32 NANDPrivateCreateDirAsync(const char *path, u8 perm, u8 attr, NANDCallback cb, NANDCommandBlock *block); + +s32 NANDMove(const char* path, const char* destDir); + +s32 NANDGetLength(NANDFileInfo* info, u32* length); +s32 NANDGetLengthAsync(NANDFileInfo* info, u32* length, NANDCallback cb, + NANDCommandBlock* block); + +s32 NANDGetStatus(const char* path, NANDStatus* stat); +s32 NANDPrivateGetStatus(const char* path, NANDStatus* stat); +s32 NANDPrivateGetStatusAsync(const char* path, NANDStatus* stat, + NANDCallback cb, NANDCommandBlock* block); + +void NANDSetUserData(NANDCommandBlock* block, void* data); +void* NANDGetUserData(const NANDCommandBlock* block); + +// NANDCore s32 NANDInit(void); +s32 NANDGetHomeDir(char[NAND_MAX_PATH]); +s32 NANDPrivateGetTypeAsync(const char* path, u8* type, NANDCallback cb, NANDCommandBlock* block); +void NANDInitBanner(NANDBanner* bnr, u32 const flag, const u16* title, const u16* comment); -s32 NANDCreate(const char*, u8, u8); -s32 NANDPrivateCreate(const char*, u8, u8); +// NANDCheck +s32 NANDCheck(const u32 fsBlock, const u32 inode, u32* answer); +// NANDOpenClose s32 NANDOpen(const char*, NANDFileInfo*, u8); s32 NANDPrivateOpen(const char*, NANDFileInfo*, u8); s32 NANDOpenAsync(const char*, NANDFileInfo*, u8, NANDCallback, NANDCommandBlock*); @@ -134,31 +180,30 @@ s32 NANDPrivateOpenAsync(const char*, NANDFileInfo*, const u8, NANDCallback, NAN s32 NANDClose(NANDFileInfo*); s32 NANDCloseAsync(NANDFileInfo*, NANDCallback, NANDCommandBlock*); -s32 NANDRead(NANDFileInfo*, void*, u32); -s32 NANDReadAsync(NANDFileInfo*, void*, u32, NANDCallback, NANDCommandBlock*); -s32 NANDGetLength(NANDFileInfo*, u32*); +s32 NANDSimpleSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length); +s32 NANDSimpleSafeClose(NANDFileInfo* info); +s32 NANDPrivateSafeOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block); +s32 NANDSafeCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block); -s32 NANDDelete(const char*); +// NANDLogging +BOOL NANDLoggingAddMessageAsync(NANDLoggingCallback cb, s32 errorCode, const char* fmt, ...); -s32 NANDMove(const char*, const char*); +// NANDErrorMessage +BOOL NANDSetAutoErrorMessaging(BOOL show); +void __NANDPrintErrorMessage(s32 errorCode); -s32 NANDCheck(u32, u32, u32*); -s32 NANDWrite(NANDFileInfo*, const void*, u32); -s32 NANDWriteAsync(NANDFileInfo*, const void*, u32, NANDCallback, NANDCommandBlock*); - -s32 NANDSeekAsync(NANDFileInfo*, s32, s32, NANDCallback, NANDCommandBlock*); - -s32 NANDPrivateGetStatus(const char*, NANDStatus*); -s32 NANDPrivateDelete(const char*); -s32 NANDPrivateCreate(const char*, u8, u8); - -s32 NANDGetHomeDir(char[NAND_MAX_PATH]); - -s32 NANDGetStatus(const char*, NANDStatus*); - -s32 NANDSecretGetUsage(const char*, u32*, u32*); +const char* nandGetHomeDir(); +void nandGenerateAbsPath(char* absPath, const char* path); +BOOL nandIsPrivatePath(const char* path); +BOOL nandIsInitialized(void); +s32 nandConvertErrorCode(const ISFSError err); +void nandGetRelativeName(char* name, const char* path); +BOOL nandIsUnderPrivatePath(const char* path); +BOOL nandIsRelativePath(const char* path); +void nandGetParentDirectory(char* parentDir, const char* absPath); +BOOL nandIsAbsolutePath(const char* path); #ifdef __cplusplus } diff --git a/include/revolution/os.h b/include/revolution/os.h index 2ae31d5c5c..62603a97aa 100644 --- a/include/revolution/os.h +++ b/include/revolution/os.h @@ -46,6 +46,7 @@ typedef u32 OSTick; #include #include #include +#include // private macro, maybe shouldn't be defined here? #define OFFSET(addr, align) (((u32)(addr) & ((align)-1))) @@ -68,16 +69,29 @@ OSThread* __OSCurrentThread AT_ADDRESS(OS_BASE_CACHED | 0x00E4); u32 __OSSimulatedMemSize AT_ADDRESS(OS_BASE_CACHED | 0x00F0); u32 __OSBusClock AT_ADDRESS(OS_BASE_CACHED | 0x00F8); u32 __OSCoreClock AT_ADDRESS(OS_BASE_CACHED | 0x00FC); -volatile u16 __OSDeviceCode AT_ADDRESS(OS_BASE_CACHED | 0x30E6); +vu16 __OSDeviceCode AT_ADDRESS(OS_BASE_CACHED | 0x30E6); +vu8 __OSLockedFlag AT_ADDRESS(OS_BASE_CACHED | 0x3187); u16 __OSWirelessPadFixMode AT_ADDRESS(OS_BASE_CACHED | 0x30E0); +vu32 __OSLaunchPartitionType AT_ADDRESS(OS_BASE_CACHED | 0x3194); +vu8 __OSDeviceCheckCode AT_ADDRESS(OS_BASE_CACHED | 0x319C); // unknowns OSThread* __gUnkThread1 AT_ADDRESS(OS_BASE_CACHED | 0x00D8); int __gUnknown800030C0[2] AT_ADDRESS(OS_BASE_CACHED | 0x30C0); u8 __gUnknown800030E3 AT_ADDRESS(OS_BASE_CACHED | 0x30E3); #else -#define __OSBusClock (*(u32 *)(OS_BASE_CACHED | 0x00F8)) -#define __OSCoreClock (*(u32 *)(OS_BASE_CACHED | 0x00FC)) +#define __OSPhysicalMemSize (*(u32*)(OS_BASE_CACHED | 0x0028)) +#define __OSTVMode (*(volatile int*)(OS_BASE_CACHED | 0x00CC)) +#define __OSActiveThreadQueue (*(OSThreadQueue*)(OS_BASE_CACHED | 0x00DC)) +#define __OSCurrentThread ((OSThread*)(OS_BASE_CACHED | 0x00E4)) +#define __OSSimulatedMemSize (*(u32*)(OS_BASE_CACHED | 0x00F0)) +#define __OSBusClock (*(u32*)(OS_BASE_CACHED | 0x00F8)) +#define __OSCoreClock (*(u32*)(OS_BASE_CACHED | 0x00FC)) +#define __OSDeviceCode (*(vu16*)(OS_BASE_CACHED | 0x30E6)) +#define __OSLockedFlag (*(vu8*)(OS_BASE_CACHED | 0x3187)) +#define __OSWirelessPadFixMode (*(u16*)(OS_BASE_CACHED | 0x30E0)) +#define __OSLaunchPartitionType (*(vu32*)(OS_BASE_CACHED | 0x3194)) +#define __OSDeviceCheckCode (*(vu8*)(OS_BASE_CACHED | 0x319C)) #endif #define OS_BUS_CLOCK __OSBusClock diff --git a/include/revolution/os/OSNet.h b/include/revolution/os/OSNet.h new file mode 100644 index 0000000000..685d82696b --- /dev/null +++ b/include/revolution/os/OSNet.h @@ -0,0 +1,29 @@ +#ifndef _REVOLUTION_OSNET_H_ +#define _REVOLUTION_OSNET_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum NWC24Err { + NWC24_OK = 0, +} NWC24Err; + +NWC24Err NWC24iPrepareShutdown(void); +NWC24Err NWC24iSynchronizeRtcCounter(BOOL); +NWC24Err NWC24iOpenResourceManager_(const char* callerName, const char* path, IOSFd* fd, u32 flags); +NWC24Err NWC24SuspendScheduler(void); +NWC24Err NWC24iSetRtcCounter_(int rtc, BOOL); +NWC24Err NWC24iIoctlResourceManager_(const char* callerName, IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen); +NWC24Err NWC24iCloseResourceManager_(const char* callerName, IOSFd); +NWC24Err NWC24iRequestShutdown(u32 event, s32* result); +NWC24Err NWC24iIoctlResourceManagerAsync_(const char* callerName, IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen, void* cbArg); +BOOL NWC24iIsAsyncRequestPending_(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/revolution/os/OSPlayRecord.h b/include/revolution/os/OSPlayRecord.h index 1cbfb15ee5..90ffde13df 100644 --- a/include/revolution/os/OSPlayRecord.h +++ b/include/revolution/os/OSPlayRecord.h @@ -3,6 +3,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { u32 checkSum; u16 titleName[2][21]; @@ -16,4 +20,8 @@ typedef struct { void __OSStartPlayRecord(void); void __OSStopPlayRecord(void); +#ifdef __cplusplus +} +#endif + #endif // OSPLAYRECORD_H diff --git a/include/revolution/os/OSReset.h b/include/revolution/os/OSReset.h index 2964bb8638..5ac53552bb 100644 --- a/include/revolution/os/OSReset.h +++ b/include/revolution/os/OSReset.h @@ -17,10 +17,10 @@ typedef struct OSShutdownFunctionQueue { OSShutdownFunctionInfo* tail; } OSShutdownFunctionQueue; -typedef BOOL (*OSResetFunction)(BOOL, u32); +typedef BOOL (*OSShutdownFunction)(BOOL, u32); struct OSShutdownFunctionInfo { - OSResetFunction func; + OSShutdownFunction func; u32 priority; OSShutdownFunctionInfo* next; OSShutdownFunctionInfo* prev; diff --git a/include/revolution/private/iosresclt.h b/include/revolution/private/iosresclt.h deleted file mode 100644 index d9fbb621c1..0000000000 --- a/include/revolution/private/iosresclt.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef IOSError (*IOSIpcCb) (IOSError, void *); - -s32 IOS_Read(s32, void*, u32); diff --git a/include/revolution/private/iosrestypes.h b/include/revolution/private/iosrestypes.h index 679ed63cf7..9f7502bd1b 100644 --- a/include/revolution/private/iosrestypes.h +++ b/include/revolution/private/iosrestypes.h @@ -33,19 +33,19 @@ typedef u32 IOSResourceHandle; #define IOS_ERROR_INVALID_SIZE -23 typedef struct { - u8 *path; + u8* path; u32 flags; IOSUid uid; IOSGid gid; } IOSResourceOpen; typedef struct { - u8 *outPtr; + u8* outPtr; u32 outLen; } IOSResourceRead; typedef struct { - u8 *inPtr; + u8* inPtr; u32 inLen; } IOSResourceWrite; @@ -56,14 +56,14 @@ typedef struct { typedef struct { u32 cmd; - u8 *inPtr; + u8* inPtr; u32 inLen; - u8 *outPtr; + u8* outPtr; u32 outLen; } IOSResourceIoctl; typedef struct { - u8 *base; + u8* base; u32 length; } IOSIoVector; @@ -71,7 +71,7 @@ typedef struct { u32 cmd; u32 readCount; u32 writeCount; - IOSIoVector *vector; + IOSIoVector* vector; } IOSResourceIoctlv; typedef struct { diff --git a/src/revolution/dvd/__dvd.h b/src/revolution/dvd/__dvd.h index 6443d85b68..c0c6cb8452 100644 --- a/src/revolution/dvd/__dvd.h +++ b/src/revolution/dvd/__dvd.h @@ -1,8 +1,8 @@ -#ifndef _DOLPHIN_DVD_INTERNAL_H_ -#define _DOLPHIN_DVD_INTERNAL_H_ +#ifndef _REVOLUTION_DVD_INTERNAL_H_ +#define _REVOLUTION_DVD_INTERNAL_H_ -#include -#include +#include +#include #ifdef __cplusplus extern "C" { @@ -11,17 +11,21 @@ extern "C" { // DVD DVDCommandChecker __DVDSetOptionalCommandChecker(DVDCommandChecker func); void __DVDSetImmCommand(u32 command); -void __DVDSetDmaCommand(u32 command); -void* __DVDGetIssueCommandAddr(void); void __DVDAudioBufferConfig(DVDCommandBlock* block, u32 enable, u32 size, DVDCBCallback callback); -void __DVDPrepareResetAsync(DVDCBCallback callback); int __DVDTestAlarm(const OSAlarm* alarm); +BOOL __DVDLowBreak(void); +u32 __DVDGetCoverStatus(void); +void __DVDPrepareReset(void); + +extern vu32 __DVDLayoutFormat; // DVD ERROR -void __DVDStoreErrorCode(u32 error); +void __DVDStoreErrorCode(u32 error, DVDCBCallback callback); +BOOL __DVDCheckDevice(void); // DVD FATAL void __DVDPrintFatalMessage(void); +void __DVDShowFatalMessage(void); // DVD FS extern OSThreadQueue __DVDThreadQueue; @@ -29,10 +33,7 @@ extern u32 __DVDLongFileNameFlag; void __DVDFSInit(void); -// DVD LOW -void __DVDInitWA(void); -void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context); -void __DVDLowSetWAType(u32 type, s32 seekLoc); +// DVD BROADWAY int __DVDLowTestAlarm(const OSAlarm* alarm); // DVD QUEUE @@ -42,15 +43,10 @@ DVDCommandBlock* __DVDPopWaitingQueue(void); int __DVDCheckWaitingQueue(void); int __DVDDequeueWaitingQueue(DVDCommandBlock* block); int __DVDIsBlockInWaitingQueue(DVDCommandBlock* block); - -// FST LOAD -void __fstLoad(void); - -// unsorted -u32 __DVDGetCoverStatus(void); +DVDCommandBlock* __DVDGetNextWaitingQueue(void); #ifdef __cplusplus } #endif -#endif // _DOLPHIN_DVD_INTERNAL_H_ +#endif // _REVOLUTION_DVD_INTERNAL_H_ diff --git a/src/revolution/dvd/dvd.c b/src/revolution/dvd/dvd.c new file mode 100644 index 0000000000..d000b71429 --- /dev/null +++ b/src/revolution/dvd/dvd.c @@ -0,0 +1,2553 @@ +#include +#include +#include +#include + +#include "os/__os.h" +#include "__dvd.h" + +// externs +extern DVDErrorInfo __ErrorInfo; + +#ifdef SDK_AUG2010 +#define BUILD_DATE "Aug 23 2010" +#if DEBUG +#define BUILD_TIME "17:24:50" +#else +#define BUILD_TIME "17:33:06" +#endif +#elif SDK_SEP2006 +#define BUILD_DATE "Sep 21 2006" +#define BUILD_TIME "14:32:13" +#endif + +#ifdef SDK_AUG2010 +#if DEBUG +const char* __DVDVersion = "<< RVL_SDK - DVD \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>"; +#else +const char* __DVDVersion = "<< RVL_SDK - DVD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>"; +#endif +#elif SDK_SEP2006 +const char* __DVDVersion = "<< RVL_SDK - DVD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>"; +#endif + +typedef void (*stateFunc)(DVDCommandBlock* block); +stateFunc LastState; + +static DVDCommandBlock* executing; +static DVDDiskID* IDShouldBe; +static OSBootInfo* bootInfo; +static vu32 CurrCommand; +static void (*CancelCallback)(s32, DVDCommandBlock*); +static vu32 CancelLastError; +static u32 LastError; +static BOOL ResetRequired; +static u32 MotorState; +static volatile OSTime LastResetEnd; +static u32 __DVDNumTmdBytes ATTRIBUTE_ALIGN(32); +static DVDGameTOC* GameToc; +static DVDPartitionInfo* PartInfo; +static DVDPartitionInfo* BootGameInfo; +static volatile BOOL Prepared; +static vu32 __BS2DVDLowIntType; +static int DVDInitialized; +vu32 __DVDLayoutFormat; +static volatile BOOL PreparingCover; +static vu32 ChangedDisc; +static vu32 MotorStopped; +static vu32 WaitingForCoverClose; +static vu32 WaitingForCoverOpen; +static vu32 Breaking; +static BOOL FirstTimeInBootrom; +static vs32 NumInternalRetry; +static vu32 ResumeFromHere; +static vu32 Canceling; +static BOOL FatalErrorFlag; +static volatile BOOL PausingFlag; +static volatile BOOL PauseFlag; + +static BOOL autoInvalidation = TRUE; +static int CancelAllSyncComplete; +volatile u32 CommandInfoCounter = 0; + +static void defaultOptionalCommandChecker(DVDCommandBlock*, DVDCommandCheckerCallback); +static DVDCommandChecker checkOptionalCommand = defaultOptionalCommandChecker; + + +static DVDBB2 BB2; +static DVDDiskID CurrDiskID; +static DVDCommandBlock DummyCommandBlock; +static OSAlarm ResetAlarm; +static OSAlarm CoverAlarm; + +static u8 __DVDGameTocBuffer[OSRoundUp32B(sizeof(DVDGameTOC) * 4)] ATTRIBUTE_ALIGN(32); +static u8 __DVDPartInfoBuffer[OSRoundUp32B(sizeof(DVDPartitionInfo) * 4)] ATTRIBUTE_ALIGN(32); +static u8 __DVDTmdBuffer[OSRoundUp32B(sizeof(ESTitleMeta))] ATTRIBUTE_ALIGN(32); +static u8 __DVDTicketViewBuffer[OSRoundUp32B(sizeof(ESTicketView))] ATTRIBUTE_ALIGN(32); + +static OSAlarm FatalAlarm; +DVDCommandBlock __DVDStopMotorCommandBlock; + +// prototypes +static void stateDownRotation(DVDCommandBlock*); +static void stateReadingTOC(DVDCommandBlock*); +static void stateReadingPartitionInfo(DVDCommandBlock* block); +static void stateOpenPartition(DVDCommandBlock* block); +static void stateOpenPartition2(DVDCommandBlock* block); +static void stateReadingFST(); +static void cbForStateReadingFST(u32 intType); +static void cbForStateError(u32 intType); +static void stateError(u32 error); +static void stateTimeout(); +static void stateSecurityError(void); +static void stateGettingError(); +static u32 CategorizeError(u32 error); +static BOOL CheckCancel(u32 resume); +static void cbForStateGettingError(u32 intType); +static void cbForUnrecoveredError(u32 intType); +static void cbForUnrecoveredErrorRetry(u32 intType); +static void stateGoToRetry(); +static void cbForStateGoToRetry(u32 intType); +static void stateCheckID(); +static void stateCheckID3(); +static void stateCheckID2a(); +static void stateCheckID2(DVDCommandBlock* block); +static void cbForStateCheckID1(u32 intType); +static void cbForStateCheckID2(u32 intType); +static void cbForStateCheckID3(u32 intType); +static void cbForStateCheckID2a(u32 intType); +static void AlarmHandler(OSAlarm* alarm, OSContext* context); +static void stateCoverClosed(); +static void stateCoverClosed_CMD(DVDCommandBlock* command); +static void cbForStateCoverClosed(u32 intType); +static void stateMotorStopped(); +static void cbForStateMotorStopped(u32 intType); +static void stateReady(); +static void stateBusy(DVDCommandBlock* block); +static BOOL IsImmCommandWithResult(u32 command); +static int IsDmaCommand(u32 command); +static void cbForStateBusy(u32 intType); +static int issueCommand(s32 prio, DVDCommandBlock* block); +static void cbForChangeDiskSync(s32 result, DVDCommandBlock* block); +static void cbForStopMotorSync(s32 result, DVDCommandBlock* block); +static void cbForInquirySync(s32 result, DVDCommandBlock* block); +static void cbForCancelSync(s32 result, DVDCommandBlock* block); +static void cbForCancelAllSync(s32 result, DVDCommandBlock* block); +static void cbForStateOpenPartition(u32 intType); +static void cbForStateReset(u32 intType); +static void cbForStateDownRotation(u32 intType); + +DECL_WEAK void StampCommand(u32 command, u32 offset, u32 length) { + BOOL enabled = OSDisableInterrupts(); + + if (CommandInfoCounter >= 5) { + CommandInfoCounter = 0; + } + + __ErrorInfo.lastCommand[CommandInfoCounter].command = command; + __ErrorInfo.lastCommand[CommandInfoCounter].offset = offset; + __ErrorInfo.lastCommand[CommandInfoCounter].length = length; + __ErrorInfo.lastCommand[CommandInfoCounter].tick = OSGetTick(); + CommandInfoCounter++; + OSRestoreInterrupts(enabled); +} + +void StampIntType(u32 intType) { + BOOL enabled = OSDisableInterrupts(); + + if (CommandInfoCounter == 0) { + __ErrorInfo.lastCommand[4].intType = intType; + } else { + __ErrorInfo.lastCommand[CommandInfoCounter - 1].intType = intType; + } + + OSRestoreInterrupts(enabled); +} + +static void defaultOptionalCommandChecker(DVDCommandBlock*, DVDCommandCheckerCallback) {} + +DVDCommandChecker __DVDSetOptionalCommandChecker(DVDCommandChecker func) { + DVDCommandChecker old = checkOptionalCommand; + checkOptionalCommand = func; + return checkOptionalCommand; +} + +void DVDInit(void) { + DVDDiskID* id; + s32 rv; + + if (!DVDInitialized) { + OSRegisterVersion(__DVDVersion); + DVDInitialized = TRUE; + DVDLowInit(); + + if (!__OSInIPL && __OSLockedFlag == 0x80) { + rv = ESP_InitLib(); + + if (rv == 0) { + rv = ESP_DiGetTicketView(NULL, (ESTicketView*)__DVDTicketViewBuffer); + } + + if (rv == 0) { + rv = ESP_DiGetTmd(NULL, &__DVDNumTmdBytes); + } + + if (rv == 0) { + rv = ESP_DiGetTmd((ESTitleMeta*)__DVDTmdBuffer, &__DVDNumTmdBytes); + } + + ESP_CloseLib(); + } + + __DVDFSInit(); + __DVDClearWaitingQueue(); + + MotorState = 0; + bootInfo = (void*)OSPhysicalToCached(0); + IDShouldBe = &bootInfo->DVDDiskID; + + OSInitThreadQueue(&__DVDThreadQueue); + + DVDLowUnmaskStatusInterrupts(); + DVDLowMaskCoverInterrupt(); + + if (bootInfo->magic == 0xE5207C22) { + + } else if (bootInfo->magic == 0x0D15EA5E) { + + } else { + FirstTimeInBootrom = TRUE; + } + + memset(&__ErrorInfo, 0, sizeof(DVDErrorInfo)); + id = (DVDDiskID*)OSPhysicalToCached(0); + memcpy(__ErrorInfo.gameName, id->gameName, 4); + __ErrorInfo.diskNumber = id->diskNumber; + __ErrorInfo.gameVersion = id->gameVersion; + __DVDLayoutFormat = 0; + DVDSetAutoFatalMessaging(TRUE); + } +} + +static void stateReadingFST() { + LastState = (stateFunc)stateReadingFST; + ASSERTLINE(1084, ((u32)(bootInfo->FSTLocation) & (32 - 1)) == 0); + DVD_ASSERTMSGLINE(1093, bootInfo->FSTMaxLength >= BB2.FSTLength, "DVDChangeDisk(): FST in the new disc is too big. "); + DVDLowClearCoverInterrupt(NULL); + StampCommand(1, BB2.FSTPosition >> __DVDLayoutFormat, OSRoundUp32B(BB2.FSTLength << (~__DVDLayoutFormat & 2))); + DVDLowRead(bootInfo->FSTLocation, OSRoundUp32B(BB2.FSTLength << (~__DVDLayoutFormat & 2)), BB2.FSTPosition >> __DVDLayoutFormat, cbForStateReadingFST); +} + +static u32 DmaCommand[1] = {0xFFFFFFFF}; + +static void cbForStateReadingFST(u32 intType) { + DVDCommandBlock* finished; + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(1125, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(1130, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + + __DVDFSInit(); + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + + if (finished->callback) { + finished->callback(0, finished); + } + + stateReady(); + return; + } + + ASSERTLINE(1157, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void FatalAlarmHandler(OSAlarm* alarm, OSContext* context) { + __DVDPrintFatalMessage(); +} + +static void cbForStateError(u32 intType) { + DVDCommandBlock* finished; + + if (__DVDGetAutoFatalMessaging()) { + OSCreateAlarm(&FatalAlarm); + OSSetAlarm(&FatalAlarm, 1, FatalAlarmHandler); + } else { + executing->state = -1; + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + FatalErrorFlag = TRUE; + finished = executing; + executing = &DummyCommandBlock; + + if (finished->callback) { + (finished->callback)(-1, finished); + } + + if (Canceling) { + Canceling = FALSE; + if (CancelCallback) + (CancelCallback)(0, finished); + } + + stateReady(); + } +} + +static void cbForStoreErrorCode1(s32 result, DVDCommandBlock* block) { + DVDLowStopMotor(FALSE, FALSE, cbForStateError); +} + +static void stateError(u32 error) { + __DVDStoreErrorCode(error, cbForStoreErrorCode1); +} + +static void cbForStoreErrorCode2(s32 result, DVDCommandBlock* block) { + DVDLowSetSpinupFlag(0); + DVDLowReset(cbForStateError); + ResetRequired = FALSE; + ResumeFromHere = 0; +} + +static void stateTimeout() { + __DVDStoreErrorCode(0x01234568, cbForStoreErrorCode2); +} + +static void stateSecurityError(void) { + __DVDStoreErrorCode(0x1234569, cbForStoreErrorCode2); +} + +static void stateGettingError() { + StampCommand(39, 0, 0); + DVDLowRequestError(cbForStateGettingError); +} + +static u32 CategorizeError(u32 error) { + if (error == 0x20400) { + LastError = error; + return 1; + } + + error &= 0x00FFFFFF; + if (error == 0x62800 || error == 0x23A00 || error == 0x53000 || error == 0xB5A01) { + return 0; + } + + if (error == 0x52000 && (executing->command == 37 || LastState == stateDownRotation)) { + return 0; + } + + NumInternalRetry++; + if (NumInternalRetry == 2) { + if (error == LastError) { + LastError = error; + return 1; + } + LastError = error; + return 2; + } + + LastError = error; + + if (error == 0x31100 || executing->command == DVD_COMMAND_READID) { + return 2; + } + + return 3; +} + +static BOOL CheckCancel(u32 resume) { + DVDCommandBlock* finished; + + if (Canceling) { + ResumeFromHere = resume; + Canceling = FALSE; + + finished = executing; + executing = &DummyCommandBlock; + + finished->state = 10; + + if (finished->callback) + (*finished->callback)(-3, finished); + + if (CancelCallback) + (CancelCallback)(0, finished); + + stateReady(); + return TRUE; + } + + return FALSE; +} + +static void cbForStoreErrorCode3(s32 result, DVDCommandBlock* block) { + stateGoToRetry(); +} + +static void cbForStateGettingError(u32 intType) { + u32 error; + u32 status; + u32 errorCategory; + u32 resume; + + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + if (intType & 2) { + stateError(0x1234567); + return; + } + + ASSERTLINE(1474, intType == DVD_INTTYPE_TC); + + error = DVDLowGetImmBufferReg(); + status = error & 0xff000000; + + errorCategory = CategorizeError(error); + + if (errorCategory == 1) { + stateError(error); + return; + } + + if (errorCategory == 2 || errorCategory == 3) { + resume = 0; + } else { + if (status == 0x1000000) { + resume = 4; + } else if (status == 0x2000000) { + resume = 6; + } else if (status == 0x3000000) { + resume = 3; + } else if (status == 0) { + if (error == 0x53000) { + resume = 1; + } + else { + resume = 5; + } + } else { + resume = 5; + } + } + + if (CheckCancel(resume)) + return; + + if (errorCategory == 2) { + __DVDStoreErrorCode(error, cbForStoreErrorCode3); + return; + } + + if (errorCategory == 3) { + if ((error & 0x00ffffff) == 0x00031100) { + StampCommand(2, executing->offset, 0); + DVDLowSeek(executing->offset, cbForUnrecoveredError); + } else { + LastState(executing); + } + return; + } + + if (status == 0x01000000) { + executing->state = 5; + stateMotorStopped(); + return; + } else if (status == 0x02000000) { + executing->state = 3; + stateCoverClosed(); + return; + } else if (status == 0x03000000) { + executing->state = 4; + stateMotorStopped(); + return; + } else if (status == 0) { + if (error == 0x53000) { + StampCommand(16, 0, 0); + DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1); + return; + } else { + stateError(0x1234567); + return; + } + } else { + stateError(0x1234567); + return; + } +} + +static void cbForUnrecoveredError(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + if (intType & 1) { + stateGoToRetry(); + return; + } + + ASSERTLINE(1606, intType == DVD_INTTYPE_DE); + StampCommand(39, 0, 0); + DVDLowRequestError(cbForUnrecoveredErrorRetry); +} + +static void cbForUnrecoveredErrorRetry(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + if (intType & 2) { + stateError(0x1234567); + } else { + stateError(DVDLowGetImmBufferReg()); + } +} + +static void stateGoToRetry() { + StampCommand(16, 0, 0); + DVDLowStopMotor(FALSE, FALSE, cbForStateGoToRetry); +} + +static void cbForStateGoToRetry(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + if (intType & 2) { + stateError(0x1234567); + return; + } + + ASSERTLINE(1673, intType == DVD_INTTYPE_TC); + NumInternalRetry = 0; + + if (CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 13 || CurrCommand == 33 || CurrCommand == 34 || CurrCommand == 41 || CurrCommand == 42 || CurrCommand == 15 || CurrCommand == 37) { + ResetRequired = TRUE; + } + + if (!CheckCancel(2)) { + executing->state = 11; + stateMotorStopped(); + } +} + +static void stateCheckID() { + switch(CurrCommand) { + case DVD_COMMAND_CHANGE_DISK: + ChangedDisc = FALSE; + + if (DVDCompareDiskID(&CurrDiskID, executing->id)) { + memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID)); + executing->state = DVD_STATE_BUSY; + DCInvalidateRange(&BB2.bootFilePosition, 0x20); + NumInternalRetry = 0; + stateReadingTOC(executing); + } else { + StampCommand(16, 0, 0); + DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1); + } + break; + default: + if (memcmp(&CurrDiskID, IDShouldBe, sizeof(DVDDiskID)) != 0) { + StampCommand(16, 0, 0); + DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1); + } else { + NumInternalRetry = 0; + stateReadingTOC(executing); + } + break; + } +} + + +static ESTitleMeta* Tmd; + +static void cbForStateReadingTOC(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(1870, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & 1) { + ASSERTLINE(1875, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + GameToc = (DVDGameTOC*)__DVDGameTocBuffer; + stateReadingPartitionInfo(executing); + } else { + ASSERTLINE(1887, intType == DVD_INTTYPE_DE); + stateGettingError(); + } +} + +static void stateReadingTOC(DVDCommandBlock* block) { + DVDLowClearCoverInterrupt(NULL); + StampCommand(33, 0x40000 >> 2, OSRoundUp32B(sizeof(DVDGameTOC))); + DVDLowUnencryptedRead(__DVDGameTocBuffer, OSRoundUp32B(sizeof(DVDGameTOC)), 0x10000, cbForStateReadingTOC); +} + +static void cbForStateReadingPartitionInfo(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(1922, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & 1) { + s16 i; + ASSERTLINE(1929, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + PartInfo = (DVDPartitionInfo*)__DVDPartInfoBuffer; + BootGameInfo = NULL; + + if (*((u32*)OSPhysicalToCached(0x3198))) { + BootGameInfo = PartInfo; + BootGameInfo->type = *((u32*)OSPhysicalToCached(0x3194)); + BootGameInfo->gamePartition = (DVDGamePartition*)*((u32*)OSPhysicalToCached(0x3198)); + } else { + for (i = 0; i < GameToc->numGamePartitions; i++) { + if (PartInfo->type == __OSLaunchPartitionType) { + BootGameInfo = PartInfo; + } + + PartInfo++; + } + } + + if (BootGameInfo) { + switch (CurrCommand) { + case 3: + NumInternalRetry = 0; + stateOpenPartition(executing); + break; + + default: + NumInternalRetry = 0; + stateOpenPartition2(executing); + break; + } + } else { + if (!CheckCancel(1)) { + executing->state = 6; + stateMotorStopped(); + } + } + } else { + ASSERTLINE(1991, intType == DVD_INTTYPE_DE); + stateGettingError(); + } +} + +static void stateReadingPartitionInfo(DVDCommandBlock* block) { + DVDLowClearCoverInterrupt(0); + StampCommand(33, (0x40000 + OSRoundUp32B(sizeof(DVDGameTOC))) >> 2, OSRoundUp32B(sizeof(DVDPartitionInfo))); + DVDLowUnencryptedRead(__DVDPartInfoBuffer, OSRoundUp32B(sizeof(DVDPartitionInfo)), (0x40000 + OSRoundUp32B(sizeof(DVDGameTOC))) >> 2, cbForStateReadingPartitionInfo); +} + +static void cbForStateOpenPartition(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(2028, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & 1) { + ASSERTLINE(2033, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateCheckID2(executing); + } else { + ASSERTLINE(2043, intType == DVD_INTTYPE_DE); + stateGettingError(); + } +} + +static void stateOpenPartition(DVDCommandBlock* block) { + DVDLowClearCoverInterrupt(0); + StampCommand(34, (u32)BootGameInfo->gamePartition, 0); + + if (__OSLockedFlag == 0x80) { + DVDLowOpenPartitionWithTmdAndTicketView((u32)BootGameInfo->gamePartition, (ESTicketView*)__DVDTicketViewBuffer, __DVDNumTmdBytes, (ESTitleMeta*)__DVDTmdBuffer, 0, NULL, cbForStateOpenPartition); + } else { + DVDLowOpenPartition((u32)BootGameInfo->gamePartition, NULL, 0, 0, (ESTitleMeta*)__DVDTmdBuffer, cbForStateOpenPartition); + } +} + +static void cbForStateOpenPartition2(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(2090, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & 1) { + ASSERTLINE(2095, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + + if (!CheckCancel(0)) { + executing->state = 1; + stateBusy(executing); + } + } else { + ASSERTLINE(2109, intType == DVD_INTTYPE_DE); + stateGettingError(); + } +} + +static void stateOpenPartition2(DVDCommandBlock* block) { + DVDLowClearCoverInterrupt(0); + StampCommand(34, (u32)BootGameInfo->gamePartition, 0); + + if (__OSLockedFlag == 0x80) { + DVDLowOpenPartitionWithTmdAndTicketView((u32)BootGameInfo->gamePartition, (ESTicketView*)__DVDTicketViewBuffer, __DVDNumTmdBytes, (ESTitleMeta*)__DVDTmdBuffer, 0, NULL, cbForStateOpenPartition2); + } else { + DVDLowOpenPartition((u32)BootGameInfo->gamePartition, NULL, 0, 0, (ESTitleMeta*)__DVDTmdBuffer, cbForStateOpenPartition2); + } +} + +static void stateCheckID2(DVDCommandBlock* block) { + DVDLowClearCoverInterrupt(0); + StampCommand(1, (u32)(0x420 >> 2), OSRoundUp32B(sizeof(DVDBB2))); + DVDLowRead(&BB2, OSRoundDown32B(sizeof(DVDBB2)), (u32)(0x420 >> 2), cbForStateCheckID2); +} + +static void stateCheckID3() { + DVDLowAudioBufferConfig(IDShouldBe->streaming, 0xA, cbForStateCheckID3); +} + +static void stateCheckID2a() { + DVDLowAudioBufferConfig(IDShouldBe->streaming, 0xA, cbForStateCheckID2a); +} + +static void cbForStateCheckID2a(u32 intType) { + if (intType == 16) { + stateTimeout(); + return; + } + + ASSERTLINE(1227, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(1232, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateCheckID2(executing); + return; + } + + ASSERTLINE(1243, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void cbForStateCheckID1(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + if (intType & DVD_INTTYPE_DE) { + stateError(0x01234567); + return; + } + + ASSERTLINE(2184, intType == DVD_INTTYPE_TC); + NumInternalRetry = 0; + + if (CheckCancel(1) == FALSE) { + executing->state = DVD_STATE_WRONG_DISK; + stateMotorStopped(); + } +} + +static void cbForStateCheckID2(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(2213, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(2218, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateReadingFST(); + return; + } + + ASSERTLINE(2234, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void cbForStateCheckID3(u32 intType) { + if (intType == 16) { + stateTimeout(); + return; + } + + ASSERTLINE(1336, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(1341, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + if (CheckCancel(0) == FALSE) { + executing->state = DVD_STATE_BUSY; + stateBusy(executing); + } + return; + } + + ASSERTLINE(1355, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void AlarmHandler(OSAlarm* alarm, OSContext* context) { + DVDReset(); + DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID)); + LastState = &stateCoverClosed_CMD; + stateCoverClosed_CMD(executing); +} + +static void stateCoverClosed() { + DVDCommandBlock* finished; + MotorState = 1; + + switch(CurrCommand) { + case DVD_COMMAND_BSREAD: + case DVD_COMMAND_READID: + case DVD_COMMAND_AUDIO_BUFFER_CONFIG: + case DVD_COMMAND_BS_CHANGE_DISK: + case 33: + case 34: + case 37: + case 41: + case 42: + __DVDClearWaitingQueue(); + finished = executing; + executing = &DummyCommandBlock; + if (finished->callback) { + finished->callback(-4, finished); + } + stateReady(); + break; + case 32: + MotorState = 0; + case 35: + case 38: + case 36: + executing->state = 1; + stateBusy(executing); + break; + case 1: + case 2: + if (__OSInIPL) { + break; + } + default: + MotorState = 0; + DVDLowSetSpinupFlag(1); + DVDLowReset(cbForStateReset); + break; + } +} + +static void ResetAlarmHandler(OSAlarm* alarm, OSContext* context) { + if (__OSDeviceCode == (u16)(0x8000 | 0x003)) { + LastState = stateDownRotation; + stateDownRotation(executing); + } else { + DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID)); + LastState = stateCoverClosed_CMD; + stateCoverClosed_CMD(executing); + } +} + +static void cbForStateReset(u32 intType) { + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(2428, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & 1) { + ASSERTLINE(2433, (intType & DVD_INTTYPE_DE) == 0); + LastResetEnd = __OSGetSystemTime(); + ResetRequired = FALSE; + ResumeFromHere = 0; + OSCreateAlarm(&ResetAlarm); + OSSetAlarm(&ResetAlarm, DVD_RESETCOVER_TIMELAG_TICKS2, ResetAlarmHandler); + } else { + ASSERTLINE(2447, intType == DVD_INTTYPE_DE); + stateGettingError(); + } +} + +static void stateDownRotation(DVDCommandBlock* block) { + DVDLowClearCoverInterrupt(0); + StampCommand(37, 0, 0); + DVDLowSetMaximumRotation(0x20000, cbForStateDownRotation); +} + +static void cbForStateDownRotation(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(2480, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & 1) { + ASSERTLINE(2485, (intType & DVD_INTTYPE_DE) == 0); + DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID)); + LastState = stateCoverClosed_CMD; + stateCoverClosed_CMD(executing); + } else { + ASSERTLINE(2494, intType == DVD_INTTYPE_DE); + stateGettingError(); + } +} + +static void stateCoverClosed_CMD(DVDCommandBlock* command) { + if (CurrCommand == 40) { + NumInternalRetry = 0; + + if (!CheckCancel(0)) { + executing->state = 1; + stateBusy(executing); + } + } else { + DVDLowClearCoverInterrupt(0); + StampCommand(5, 0, sizeof(DVDDiskID)); + DVDLowReadDiskID(&CurrDiskID, cbForStateCoverClosed); + } +} + +static void cbForStateCoverClosed(u32 intType) { + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + ASSERTLINE(2542, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(2547, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateCheckID(); + return; + } + + ASSERTLINE(2559, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void cbForPrepareCoverRegister(u32 intType) { + PreparingCover = FALSE; + + if (WaitingForCoverClose) { + if (!(DVDLowGetCoverRegister() & 1)) { + OSCancelAlarm(&CoverAlarm); + WaitingForCoverClose = FALSE; + cbForStateMotorStopped(4); + } + return; + } + + if (DVDLowGetCoverRegister() & 1) { + WaitingForCoverOpen = FALSE; + WaitingForCoverClose = TRUE; + + if (MotorState == 2) { + executing->state = 12; + } + else { + executing->state = 5; + } + } else if (DVDLowGetCoverRegister() & 4) { + OSCancelAlarm(&CoverAlarm); + WaitingForCoverOpen = FALSE; + DVDLowClearCoverInterrupt(0); + cbForStateMotorStopped(4); + } +} + +static void CoverAlarmHandler(OSAlarm* alarm, OSContext* context) { + if (!PreparingCover) { + PreparingCover = TRUE; + DVDLowPrepareCoverRegister(cbForPrepareCoverRegister); + } +} + +static void stateMotorStopped() { + MotorState = 1; + + if (WaitingForCoverOpen || WaitingForCoverClose) { + return; + } + + WaitingForCoverOpen = TRUE; + OSCreateAlarm(&CoverAlarm); + OSSetPeriodicAlarm(&CoverAlarm, OSGetTick(), OSMillisecondsToTicks(100), CoverAlarmHandler); +} + +static void cbForStateMotorStopped(u32 intType) { + ASSERTLINE(2685, intType == DVD_INTTYPE_CVR); + WaitingForCoverClose = FALSE; + + if (CurrCommand == 3) { + ChangedDisc = TRUE; + } + + if (MotorState == 2) { + if (executing) { + executing->state = 12; + } + + return; + } + + DVDLowMaskCoverInterrupt(); + + if (executing) { + executing->state = 3; + stateCoverClosed(); + } else { + ResumeFromHere = 7; + } +} + +static void stateReady() { + DVDCommandBlock* finished; + + if (PauseFlag != 0) { + PausingFlag = 1; + executing = NULL; + return; + } + + if (__DVDCheckWaitingQueue() == 0) { + executing = NULL; + return; + } + + executing = __DVDPopWaitingQueue(); + + if (FatalErrorFlag) { + executing->state = DVD_STATE_FATAL_ERROR; + finished = executing; + executing = &DummyCommandBlock; + if (finished->callback) { + (*finished->callback)(-1, finished); + } + + stateReady(); + return; + } + + CurrCommand = executing->command; + if (CurrCommand == 32 || CurrCommand == 14 || CurrCommand == 35) { + ResumeFromHere = 0; + } + + if (ResumeFromHere != 0) { + switch (ResumeFromHere) { + case 2: + executing->state = DVD_STATE_RETRY; + stateMotorStopped(); + break; + case 3: + executing->state = DVD_STATE_NO_DISK; + stateMotorStopped(); + break; + case 4: + executing->state = DVD_STATE_COVER_OPEN; + stateMotorStopped(); + break; + case 1: + case 6: + case 7: + executing->state = DVD_STATE_COVER_CLOSED; + stateCoverClosed(); + break; + case 5: + stateError(CancelLastError); + break; + } + + ResumeFromHere = 0; + return; + } + + switch (MotorState) { + case 2: + if (MotorStopped) { + executing->state = 12; + } + else { + executing->state = 3; + stateCoverClosed(); + } + break; + case 0: + executing->state = 1; + stateBusy(executing); + break; + case 1: + default: + stateCoverClosed(); + break; + } +} + +static void stateBusy(DVDCommandBlock* block) { + DVDCommandBlock* finished; + LastState = stateBusy; + + switch (block->command) { + case 5: + case 2: + case 3: + case 15: + case 14: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 16: + case 34: + case 37: + case 41: + case 42: + StampCommand(block->command, block->offset, block->length); + break; + default: + break; + } + + switch (block->command) { + case 5: + DVDLowClearCoverInterrupt(0); + block->currTransferSize = sizeof(DVDDiskID); + DVDLowReadDiskID(block->addr, cbForStateBusy); + break; + case 1: + case 4: + if (block->length == 0) { + finished = executing; + executing = &DummyCommandBlock; + finished->state = 0; + + if (finished->callback) { + (finished->callback)(0, finished); + } + + stateReady(); + } else { + DVDLowClearCoverInterrupt(0); + block->currTransferSize = (block->length - block->transferredSize > 0x80000) ? 0x80000 : (block->length - block->transferredSize); + StampCommand(block->command, ((block->offset) + (block->transferredSize >> 2)), block->currTransferSize); + DVDLowRead((void*)((u8*)block->addr + block->transferredSize), block->currTransferSize, ((block->offset) + (block->transferredSize >> 2)), cbForStateBusy); + } + break; + case 2: + DVDLowClearCoverInterrupt(0); + DVDLowSeek(block->offset, cbForStateBusy); + break; + case 3: + DVDLowStopMotor(FALSE, FALSE, cbForStateBusy); + break; + case 15: + DVDLowStopMotor(FALSE, FALSE, cbForStateBusy); + break; + case 13: + DVDLowClearCoverInterrupt(0); + DVDLowAudioBufferConfig(block->offset, block->length, cbForStateBusy); + break; + case 14: + DVDLowClearCoverInterrupt(0); + block->currTransferSize = sizeof(DVDDriveInfo); + DVDLowInquiry(block->addr, cbForStateBusy); + break; + case 16: + DVDLowClearCoverInterrupt(0); + DVDLowStopMotor(FALSE, FALSE, cbForStateBusy); + break; + case 32: + DVDLowSetSpinupFlag(1); + DVDLowReset(cbForStateBusy); + break; + case 33: + if (block->length == 0) { + finished = executing; + executing = &DummyCommandBlock; + finished->state = 0; + + if (finished->callback) { + (finished->callback)(0, finished); + } + + stateReady(); + } else { + DVDLowClearCoverInterrupt(0); + block->currTransferSize = (block->length - block->transferredSize > 0x80000) ? 0x80000 : (block->length - block->transferredSize); + StampCommand(block->command, ((block->offset) + (block->transferredSize >> 2)), block->currTransferSize); + DVDLowUnencryptedRead((void*)((u8*)block->addr + block->transferredSize), block->currTransferSize, ((block->offset) + (block->transferredSize >> 2)), cbForStateBusy); + } + break; + case 34: + DVDLowClearCoverInterrupt(0); + DVDLowOpenPartition(block->offset, NULL, 0, 0, (ESTitleMeta*)block->addr, cbForStateBusy); + break; + case 35: + DVDLowClearCoverInterrupt(0); + DVDLowClosePartition(cbForStateBusy); + break; + case 38: + DVDLowPrepareCoverRegister(cbForStateBusy); + break; + case 36: + DVDLowPrepareCoverRegister(cbForStateBusy); + break; + case 37: + DVDLowClearCoverInterrupt(0); + DVDLowSetMaximumRotation(0x20000, cbForStateBusy); + break; + case 40: + DVDLowClearCoverInterrupt(0); + block->addr = &CurrDiskID; + block->currTransferSize = sizeof(DVDDiskID); + DVDLowReadDiskID(block->addr, cbForStateBusy); + break; + case 41: + { + DVDPartitionParams* params; + DVDLowClearCoverInterrupt(0); + params = block->addr; + + if (!params->numTmdBytes && !params->numCertBytes) { + DVDLowGetNoDiscBufferSizes(block->offset, ¶ms->numTmdBytes, ¶ms->numCertBytes, cbForStateBusy); + } else { + DVDLowGetNoDiscOpenPartitionParams(block->offset, ¶ms->ticket, ¶ms->numTmdBytes, + ¶ms->tmd, ¶ms->numCertBytes, params->certificates, + ¶ms->dataWordOffset, params->h3Hash, cbForStateBusy); + } + break; + } + case 42: + { + DVDPartitionParams* params; + DVDLowClearCoverInterrupt(0); + params = block->addr; + DVDLowOpenPartitionWithTmdAndTicketView(block->offset, ¶ms->ticketView, params->numTmdBytes, ¶ms->tmd, + params->numCertBytes, params->certificates, cbForStateBusy); + break; + } + default: + checkOptionalCommand(block, cbForStateBusy); + break; + } +} + +static u32 ImmCommand[3] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + +void __DVDSetImmCommand(u32 command) { + static u32 immCount; + ASSERTLINE(1790, immCount < sizeof(ImmCommand)/sizeof(ImmCommand[0])); + ImmCommand[immCount++] = command; +} + +void __DVDSetDmaCommand(u32 command) { + static u32 dmaCount; + ASSERTLINE(1798, dmaCount < sizeof(DmaCommand)/sizeof(DmaCommand[0])); + DmaCommand[dmaCount++] = command; +} + +static BOOL IsImmCommandWithResult(u32 command) { + u32 i; + + if (command == 9 || command == 10 || command == 11 || command == 12) { + return 1; + } + + for (i = 0; i < 3; i++) { + if (command == ImmCommand[i]) { + return TRUE; + } + } + + return FALSE; +} + +static int IsDmaCommand(u32 command) { + u32 i; + + if (command == 1 || command == 4 || command == 5 || command == 33 || command == 14) { + return 1; + } + + for (i = 0; i < 1; i++) { + if (command == DmaCommand[i]) { + return TRUE; + } + } + + return FALSE; +} + +static void cbForStateBusy(u32 intType) { + DVDCommandBlock* finished; + + StampIntType(intType); + + if (intType == 16) { + stateTimeout(); + return; + } + + if (intType == 32) { + stateSecurityError(); + return; + } + + if ((CurrCommand == DVD_COMMAND_CHANGE_DISK) || (CurrCommand == DVD_COMMAND_BS_CHANGE_DISK)) { + if (intType & DVD_INTTYPE_DE) { + stateError(0x01234567); + return; + } + + ASSERTLINE(3278, intType == DVD_INTTYPE_TC); + NumInternalRetry = 0; + + if (CurrCommand == DVD_COMMAND_BS_CHANGE_DISK) { + ResetRequired = 1; + } + + if (CheckCancel(7)) { + return; + } + + if (MotorState != 2) { + executing->state = 7; + stateMotorStopped(); + } + return; + } + + ASSERTLINE(3301, (intType & DVD_INTTYPE_CVR) == 0); + + if (IsDmaCommand(CurrCommand)) { + executing->transferredSize += (intType & (8 | 1)) ? executing->currTransferSize : 0; + } + + if (Breaking) { + Breaking = FALSE; + Canceling = 0; + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_CANCELED; + + if (finished->callback) { + finished->callback(-3, finished); + } + + if (CancelCallback) { + CancelCallback(0, finished); + } + + stateReady(); + return; + } + + if (intType & 1) { + ASSERTLINE(3345, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + + if (CurrCommand == 0x10) { + if (executing->offset) { + MotorState = 2; + } else { + MotorState = 1; + } + + finished = executing; + executing = &DummyCommandBlock; + finished->state = 0; + + if (finished->callback != 0) { + (*finished->callback)(0, finished); + } + + stateReady(); + return; + } + + if (CurrCommand == 32) { + LastResetEnd = __OSGetSystemTime(); + + ResetRequired = FALSE; + ResumeFromHere = 0; + + finished = executing; + executing = &DummyCommandBlock; + + finished->state = 0; + if (finished->callback) { + (finished->callback)(0, finished); + } + + stateReady(); + return; + } + + if (CheckCancel(0) != FALSE) { + return; + } + + if (CurrCommand == 38) { + s32 retVal; + u32 coverReg; + + coverReg = DVDLowGetCoverRegister(); + + if (__OSGetSystemTime() - LastResetEnd < DVD_RESETCOVER_TIMELAG_TICKS2) { + retVal = 0; + } else if (coverReg & 1) { + retVal = 1; + } else { + retVal = 2; + } + + finished = executing; + executing = &DummyCommandBlock; + + finished->state = 0; + finished->offset = (u32)retVal; + if (finished->callback) { + (finished->callback)(retVal, finished); + } + + stateReady(); + return; + } + + if (CurrCommand == 36) { + s32 retVal; + u32 coverReg; + + coverReg = DVDLowGetCoverRegister(); + if ((((u32)(coverReg) & 0x00000004) >> 2) || (coverReg & 1)) { + retVal = FALSE; + } else { + if (ResumeFromHere != 0) { + retVal = FALSE; + } + else { + retVal = TRUE; + } + } + + finished = executing; + executing = &DummyCommandBlock; + + finished->state = 0; + finished->offset = (u32)retVal; + + if (finished->callback) { + (finished->callback)(retVal, finished); + } + + stateReady(); + return; + } + + if (CurrCommand == 40) { + if (DVDCompareDiskID(&CurrDiskID, executing->id)) { + memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID)); + finished = executing; + executing = &DummyCommandBlock; + + finished->state = 0; + finished->offset = (u32)TRUE; + if (finished->callback) { + (finished->callback)(TRUE, finished); + } + + NumInternalRetry = 0; + + stateReady(); + return; + } else { + StampCommand(16, 0, 0); + DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1); + return; + } + } + + if (CurrCommand == 41) { + DVDPartitionParams* params; + + params = (DVDPartitionParams*)(executing->addr); + if (!params->dataWordOffset) { + stateBusy(executing); + return; + } + + finished = executing; + executing = &DummyCommandBlock; + + finished->state = 0; + if (finished->callback) { + (finished->callback)(0, finished); + } + + stateReady(); + return; + } + + if (IsDmaCommand(CurrCommand)) { + if (executing->transferredSize != executing->length) { + stateBusy(executing); + return; + } + + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(finished->transferredSize, finished); + } + + stateReady(); + return; + } else if (IsImmCommandWithResult(CurrCommand)) { + s32 result; + if (CurrCommand == DVD_COMMAND_REQUEST_START_ADDR || CurrCommand == DVD_COMMAND_REQUEST_PLAY_ADDR) { + result = (s32)(DVDLowGetImmBufferReg() << 2); + } else { + result = (s32)DVDLowGetImmBufferReg(); + } + + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(result, finished); + } + + stateReady(); + return; + } else { + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(0, finished); + } + + stateReady(); + return; + } + } else { + ASSERTLINE(3676, intType == DVD_INTTYPE_DE); + + if (CurrCommand == 14) { + stateError(0x01234567); + return; + } + + if ((CurrCommand == 1 || CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 33 || CurrCommand == 14) + && (executing->transferredSize == executing->length)) { + + if (CheckCancel(0)) { + return; + } + finished = executing; + executing = &DummyCommandBlock; + + finished->state = DVD_STATE_END; + if (finished->callback) { + (finished->callback)((s32)finished->transferredSize, finished); + } + stateReady(); + return; + } + + stateGettingError(); + } +} + +void* __DVDGetIssueCommandAddr(void) { + return issueCommand; +} + +static int issueCommand(s32 prio, DVDCommandBlock* block) { + BOOL level; + int result; + + if (autoInvalidation != 0 && (block->command == DVD_COMMAND_READ || block->command == DVD_COMMAND_BSREAD + || block->command == DVD_COMMAND_READID || block->command == 33 || block->command == DVD_COMMAND_INQUIRY)) { + DCInvalidateRange(block->addr, block->length); + } + + level = OSDisableInterrupts(); +#if DEBUG + if (executing == block || (block->state == DVD_STATE_WAITING && __DVDIsBlockInWaitingQueue(block))) { + OSPanic(__FILE__, 3764, "DVD library: Specified command block (or file info) is already in use\n"); + } +#endif + + block->state = DVD_STATE_WAITING; + result = __DVDPushWaitingQueue(prio, block); + if (executing == NULL && PauseFlag == 0) { + stateReady(); + } + + OSRestoreInterrupts(level); + return result; +} + +int DVDReadAbsAsyncPrio(DVDCommandBlock* block, void* addr, s32 length, s32 offset, DVDCBCallback callback, s32 prio) { + int idle; + + ASSERTMSGLINE(3804, block, "DVDReadAbsAsync(): null pointer is specified to command block address."); + ASSERTMSGLINE(3805, addr, "DVDReadAbsAsync(): null pointer is specified to addr."); + ASSERTMSGLINE(3807, !OFFSET(addr, 32), "DVDReadAbsAsync(): address must be aligned with 32 byte boundary."); + ASSERTMSGLINE(3809, !(length & (32-1)), "DVDReadAbsAsync(): length must be a multiple of 32."); + + block->command = DVD_COMMAND_READ; + block->addr = addr; + block->length = length; + block->offset = offset; + block->transferredSize = 0; + block->callback = callback; + + idle = issueCommand(prio, block); + ASSERTMSGLINE(3819, idle, "DVDReadAbsAsync(): command block is used for processing previous request."); + return idle; +} + +int DVDSeekAbsAsyncPrio(DVDCommandBlock* block, s32 offset, DVDCBCallback callback, s32 prio) { + int idle; + + ASSERTMSGLINE(2233, block, "DVDSeekAbs(): null pointer is specified to command block address."); + + block->command = DVD_COMMAND_SEEK; + block->offset = offset; + block->callback = callback; + + idle = issueCommand(prio, block); + ASSERTMSGLINE(2242, idle, "DVDSeekAbs(): command block is used for processing previous request."); + return idle; +} + +int DVDReadAbsAsyncForBS(DVDCommandBlock* block, void* addr, s32 length, s32 offset, DVDCBCallback callback) { + int idle; + + ASSERTMSGLINE(2272, block, "DVDReadAbsAsyncForBS(): null pointer is specified to command block address."); + ASSERTMSGLINE(2273, addr, "DVDReadAbsAsyncForBS(): null pointer is specified to addr."); + ASSERTMSGLINE(2275, !OFFSET(addr, 32), "DVDReadAbsAsyncForBS(): address must be aligned with 32 byte boundary."); + ASSERTMSGLINE(2277, !(length & (32-1)), "DVDReadAbsAsyncForBS(): length must be a multiple of 32."); + + block->command = DVD_COMMAND_BSREAD; + block->addr = addr; + block->length = length; + block->offset = offset; + block->transferredSize = 0; + block->callback = callback; + + idle = issueCommand(2, block); + ASSERTMSGLINE(2289, idle, "DVDReadAbsAsyncForBS(): command block is used for processing previous request."); + return idle; +} + +int DVDReadDiskID(DVDCommandBlock* block, DVDDiskID* diskID, DVDCBCallback callback) { + int idle; + + ASSERTMSGLINE(2312, block, "DVDReadDiskID(): null pointer is specified to command block address."); + ASSERTMSGLINE(2313, diskID, "DVDReadDiskID(): null pointer is specified to id address."); + ASSERTMSGLINE(2315, !OFFSET(diskID, 32), "DVDReadDiskID(): id must be aligned with 32 byte boundary."); + + block->command = DVD_COMMAND_READID; + block->addr = diskID; + block->length = 0x20; + block->offset = 0; + block->transferredSize = 0; + block->callback = callback; + + idle = issueCommand(2, block); + ASSERTMSGLINE(2325, idle, "DVDReadDiskID(): command block is used for processing previous request."); + return idle; +} + +void __DVDAudioBufferConfig(DVDCommandBlock* block, u32 enable, u32 size, DVDCBCallback callback) { + int idle; + + block->command = DVD_COMMAND_AUDIO_BUFFER_CONFIG; + block->offset = enable; + block->length = size; + block->callback = callback; + idle = issueCommand(2, block); +} + +int DVDChangeDiskAsyncForBS(DVDCommandBlock* block, DVDCBCallback callback) { + int idle; + + ASSERTMSGLINE(2869, block, "DVDChangeDiskAsyncForBS(): null pointer is specified to command block address."); + + block->command = DVD_COMMAND_BS_CHANGE_DISK; + block->callback = callback; + idle = issueCommand(2, block); + ASSERTMSGLINE(2875, idle, "DVDChangeDiskAsyncForBS(): command block is used for processing previous request."); + return idle; +} + +int DVDChangeDiskAsync(DVDCommandBlock* block, DVDDiskID* id, DVDCBCallback callback) { + int idle; + + ASSERTMSGLINE(2896, block, "DVDChangeDisk(): null pointer is specified to command block address."); + ASSERTMSGLINE(2897, id, "DVDChangeDisk(): null pointer is specified to id address."); + + if (id->company[0] == 0) { + OSReport("DVDChangeDiskAsync(): You can't specify NULL to company name. \n"); + DVD_ASSERTMSGLINE(2902, 0, ""); + } + + block->command = DVD_COMMAND_CHANGE_DISK; + block->id = id; + block->callback = callback; + DCInvalidateRange(bootInfo->FSTLocation, bootInfo->FSTMaxLength); + + idle = issueCommand(2, block); + ASSERTMSGLINE(2913, idle, "DVDChangeDisk(): command block is used for processing previous request."); + return idle; +} + +s32 DVDChangeDisk(DVDCommandBlock* block, DVDDiskID* id) { + int result; + s32 state; + BOOL enabled; + s32 retVal; + + result = DVDChangeDiskAsync(block, id, cbForChangeDiskSync); + if (result == 0) { + return -1; + } + + enabled = OSDisableInterrupts(); + while (1) { + state = block->state; + if (state == DVD_STATE_END) { + retVal = 0; + break; + } else if (state == DVD_STATE_FATAL_ERROR) { + retVal = -1; + break; + } else if (state == DVD_STATE_CANCELED) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForChangeDiskSync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDStopMotorAsync(DVDCommandBlock* block, DVDCBCallback callback) { + int idle; + ASSERTMSGLINE(2996, block, "DVDStopMotor(): Null address was specified for block"); + + block->command = DVD_COMMAND_UNK_16; + block->callback = callback; + + idle = issueCommand(2, block); + ASSERTMSGLINE(3002, idle, "DVDStopMotor(): command block is used for processing previous request."); + return idle; +} + +s32 DVDStopMotor(DVDCommandBlock* block) { + int result; + s32 state; + BOOL enabled; + s32 retVal; + + result = DVDStopMotorAsync(block, cbForStopMotorSync); + if (result == 0) { + return -1; + } + + enabled = OSDisableInterrupts(); + while (1) { + state = block->state; + if (state == DVD_STATE_END) { + retVal = 0; + break; + } else if (state == DVD_STATE_FATAL_ERROR) { + retVal = -1; + break; + } else if (state == DVD_STATE_CANCELED) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForStopMotorSync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDInquiryAsync(DVDCommandBlock* block, DVDDriveInfo* info, DVDCBCallback callback) { + int idle; + + ASSERTMSGLINE(4706, block, "DVDInquiry(): Null address was specified for block"); + ASSERTMSGLINE(4707, info, "DVDInquiry(): Null address was specified for info"); + ASSERTMSGLINE(4709, !OFFSET(info, 32), "DVDInquiry(): Address for info is not 32 bytes aligned"); + + block->command = DVD_COMMAND_INQUIRY; + block->addr = info; + block->length = 0x20; + block->transferredSize = 0; + block->callback = callback; + idle = issueCommand(2, block); + return idle; +} + +s32 DVDInquiry(DVDCommandBlock* block, DVDDriveInfo* info) { + int result; + s32 state; + BOOL enabled; + s32 retVal; + + result = DVDInquiryAsync(block, info, cbForInquirySync); + if (result == 0) { + return -1; + } + + enabled = OSDisableInterrupts(); + while (1) { + state = block->state; + if (state == DVD_STATE_END) { + retVal = (u32)block->transferredSize; + break; + } else if (state == DVD_STATE_FATAL_ERROR) { + retVal = -1; + break; + } else if (state == DVD_STATE_CANCELED) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForInquirySync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +void DVDReset(void) { + //DVDLowReset(); + __DIRegs[0] = 0x2A; + __DIRegs[1] = __DIRegs[1]; + ResetRequired = 0; + ResumeFromHere = 0; +} + +int DVDResetRequired(void) { + return ResetRequired; +} + +s32 DVDGetCommandBlockStatus(const DVDCommandBlock* block) { + BOOL enabled; + s32 retVal; + DVDCommandBlock* next; + + ASSERTMSGLINE(4907, block, "DVDGetCommandBlockStatus(): null pointer is specified to command block address."); + enabled = OSDisableInterrupts(); + + if (((volatile DVDCommandBlock*)block)->state == 3) { + retVal = 1; + } else if (((volatile DVDCommandBlock*)block)->state == 5) { + retVal = 4; + } else if (executing == &__DVDStopMotorCommandBlock) { + next = __DVDGetNextWaitingQueue(); + if (next) { + if(block == next) { + retVal = 1; + } else { + retVal = ((volatile DVDCommandBlock*)block)->state; + } + } else { + if (block == &__DVDStopMotorCommandBlock) { + retVal = 0; + } else { + retVal = ((volatile DVDCommandBlock*)block)->state; + } + } + } else { + retVal = ((volatile DVDCommandBlock*)block)->state; + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +s32 DVDGetDriveStatus(void) { + BOOL enabled = OSDisableInterrupts(); + s32 retVal; + + if (FatalErrorFlag != FALSE) { + retVal = DVD_STATE_FATAL_ERROR; + } else { + if (PausingFlag != FALSE) { + retVal = DVD_STATE_PAUSING; + } else { + if (executing == NULL) { + retVal = DVD_STATE_END; + } else if (executing == &DummyCommandBlock) { + retVal = DVD_STATE_END; + } else { + retVal = DVDGetCommandBlockStatus((DVDCommandBlock*)executing); + } + } + } + OSRestoreInterrupts(enabled); + return retVal; +} + +BOOL DVDSetAutoInvalidation(BOOL autoInval) { + BOOL prev; + + prev = autoInvalidation; + autoInvalidation = autoInval; + return prev; +} + +void DVDPause(void) { + BOOL level; + + level = OSDisableInterrupts(); + PauseFlag = 1; + if (executing == NULL) { + PausingFlag = 1; + } + OSRestoreInterrupts(level); +} + +void DVDResume(void) { + BOOL level; + + level = OSDisableInterrupts(); + PauseFlag = 0; + if (PausingFlag != 0) { + PausingFlag = 0; + stateReady(); + } + OSRestoreInterrupts(level); +} + +int DVDCancelAsync(DVDCommandBlock* block, DVDCBCallback callback) { + BOOL enabled; + DVDCommandBlock* finished; + + enabled = OSDisableInterrupts(); + + switch (block->state) { + case DVD_STATE_FATAL_ERROR: + case DVD_STATE_END: + case DVD_STATE_CANCELED: + if (callback) + (*callback)(0, block); + break; + case DVD_STATE_BUSY: + if (Canceling) { + OSRestoreInterrupts(enabled); + return FALSE; + } + + Canceling = TRUE; + CancelCallback = callback; + if (block->command == 4 || block->command == 33 || block->command == 34 || block->command == 41 || block->command == 42 || block->command == 1) { + __DVDLowBreak(); + } + break; + case DVD_STATE_WAITING: + __DVDDequeueWaitingQueue(block); + block->state = DVD_STATE_CANCELED; + + if (block->callback) + (block->callback)(-3, block); + + if (callback) + (*callback)(0, block); + break; + case DVD_STATE_COVER_CLOSED: + switch (block->command) { + case 5: + case 4: + case 13: + case 15: + case 33: + case 34: + case 37: + case 41: + case 42: + if (callback) + (*callback)(0, block); + break; + case 1: + case 2: + if (__OSInIPL) { + finished = executing; + executing = &DummyCommandBlock; + block->state = 10; + + if (block->callback) { + (block->callback)(-3, block); + } + + if (callback) { + (*callback)(0, block); + } + + stateReady(); + break; + } + default: + if (Canceling) { + OSRestoreInterrupts(enabled); + return FALSE; + } + Canceling = TRUE; + CancelCallback = callback; + break; + } + break; + case DVD_STATE_NO_DISK: + case DVD_STATE_COVER_OPEN: + case DVD_STATE_WRONG_DISK: + case DVD_STATE_MOTOR_STOPPED: + case DVD_STATE_RETRY: + if (!(WaitingForCoverClose || WaitingForCoverOpen)) { + OSRestoreInterrupts(enabled); + return FALSE; + } + + if (WaitingForCoverOpen) { + OSCancelAlarm(&CoverAlarm); + WaitingForCoverOpen = FALSE; + } + + if (block->state == DVD_STATE_NO_DISK) + ResumeFromHere = 3; + if (block->state == DVD_STATE_COVER_OPEN) + ResumeFromHere = 4; + if (block->state == DVD_STATE_WRONG_DISK) + ResumeFromHere = 1; + if (block->state == DVD_STATE_RETRY) + ResumeFromHere = 2; + if (block->state == DVD_STATE_MOTOR_STOPPED) + ResumeFromHere = 7; + + finished = executing; + executing = &DummyCommandBlock; + + block->state = DVD_STATE_CANCELED; + if (block->callback) { + (block->callback)(-3, block); + } + + if (callback) { + (callback)(0, block); + } + stateReady(); + break; + case 12: + finished = executing; + executing = &DummyCommandBlock; + block->state = 10; + + if (block->callback) { + (block->callback)(-3, block); + } + + if (callback) { + (callback)(0, block); + } + + stateReady(); + break; + } + + OSRestoreInterrupts(enabled); + return TRUE; +} + +s32 DVDCancel(DVDCommandBlock* block) { + int result; + s32 state; + u32 command; + BOOL enabled; + + result = DVDCancelAsync((void*)block, cbForCancelSync); + if (result == 0) { + return -1; + } + + enabled = OSDisableInterrupts(); + while (1) { + state = ((volatile DVDCommandBlock*)block)->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + break; + } + + if (state == DVD_STATE_COVER_CLOSED) { + command = ((volatile DVDCommandBlock*)block)->command; + if ((command == 4) || (command == 5) || (command == 13) || (command == 33) || (command == 34) || (command == 41) || + (command == 42) || (command == 15) || (command == 37)) { + break; + } + } + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return 0; +} + +static void cbForCancelSync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDCancelAllAsync(DVDCBCallback callback) { + BOOL enabled; + DVDCommandBlock* p; + int retVal; + + enabled = OSDisableInterrupts(); + DVDPause(); + while ((p = __DVDPopWaitingQueue())) { + DVDCancelAsync(p, NULL); + } + + if (executing) { + retVal = DVDCancelAsync(executing, callback); + } else { + retVal = 1; + if (callback) { + callback(0, NULL); + } + } + + DVDResume(); + OSRestoreInterrupts(enabled); + return retVal; +} + +s32 DVDCancelAll(void) { + int result; + BOOL enabled; + + enabled = OSDisableInterrupts(); + CancelAllSyncComplete = 0; + result = DVDCancelAllAsync(cbForCancelAllSync); + if (result == 0) { + OSRestoreInterrupts(enabled); + return -1; + } + + while (1) { + if (CancelAllSyncComplete == 0) { + OSSleepThread(&__DVDThreadQueue); + } else { + break; + } + } + + OSRestoreInterrupts(enabled); + return 0; +} + +static void cbForCancelAllSync(s32 result, DVDCommandBlock* block) { + CancelAllSyncComplete = 1; + OSWakeupThread(&__DVDThreadQueue); +} + +DVDDiskID* DVDGetCurrentDiskID(void) { + return (void*)OSPhysicalToCached(0); +} + +static void __BS2DVDLowCallback(u32 type) { + __BS2DVDLowIntType = type; +} + +u32 __DVDGetCoverStatus(void) { + u32 reg; + __BS2DVDLowIntType = 0; + DVDLowPrepareCoverRegister(__BS2DVDLowCallback); + + while (!__BS2DVDLowIntType) { + + } + + if (!(__BS2DVDLowIntType & 1)) { + return 0; + } + + reg = DVDLowGetCoverRegister(); + + if (__OSGetSystemTime() - LastResetEnd < DVD_RESETCOVER_TIMELAG_TICKS2) { + return 0; + } else if (reg & 1) { + return 1; + } else { + return 2; + } +} + +BOOL DVDCheckDiskAsync(DVDCommandBlock* block, DVDCBCallback callback) { + BOOL enabled; + s32 retVal, state; + + enabled = OSDisableInterrupts(); + + if (FatalErrorFlag) { + state = -1; + } else if (PausingFlag) { + state = 8; + } else { + if (WaitingForCoverOpen) { + state = 7; + } + + if (WaitingForCoverClose) { + state = 5; + } else if (executing == NULL) { + switch (ResumeFromHere) { + case 3: { + state = 4; + break; + } + case 4: { + state = 5; + break; + } + case 1: { + state = 6; + break; + } + case 2: { + state = 11; + break; + } + case 7: { + state = 7; + break; + } + default: { + state = 0; + break; + } + } + } else if (executing == &DummyCommandBlock) { + state = 0; + } else { + state = executing->state; + } + } + + retVal = TRUE; + switch (state) { + case 1: + case 9: + case 10: + case 2: + block->state = 0; + if (callback) { + (*callback)(TRUE, block); + } + OSRestoreInterrupts(enabled); + break; + case -1: + case 11: + case 7: + case 3: + case 4: + case 5: + case 6: + case 12: + block->state = 0; + if (callback) { + (*callback)(FALSE, block); + } + OSRestoreInterrupts(enabled); + break; + case 0: + case 8: + OSRestoreInterrupts(enabled); + + block->command = 36; + block->callback = callback; + + retVal = issueCommand(2, block); + break; + } + + return retVal; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +void DVDPrepareDisk() { + OSReport("DVDPrepareDisk(): null pointer is specified to command block address."); + OSReport("DVDPrepareDisk(): null pointer is specified to id address."); +} + +// NOTE: function doesn't exist in TP debug, needed for string data +void DVDPrepareDiskAsync() { + OSReport("DVDPrepareDiskAsync(): You can't call this API from DVD application. \n"); + OSReport("DVDPrepareDiskAsync(): You can't specify NULL to company name. \n"); + OSReport("DVDPrepareDisk(): command block is used for processing previous request."); +} + +BOOL DVDCheckDisk(void) { + BOOL enabled; + s32 retVal; + s32 state; + u32 coverReg; + + enabled = OSDisableInterrupts(); + + if (FatalErrorFlag) { + state = -1; + } else if (PausingFlag) { + state = 8; + } else { + if (executing == NULL) { + state = 0; + } else if (executing == &DummyCommandBlock) { + state = 0; + } else { + state = executing->state; + } + } + + switch (state) { + case DVD_STATE_BUSY: + case DVD_STATE_IGNORED: + case DVD_STATE_CANCELED: + case DVD_STATE_WAITING: + retVal = TRUE; + break; + case DVD_STATE_FATAL_ERROR: + case DVD_STATE_RETRY: + case DVD_STATE_MOTOR_STOPPED: + case DVD_STATE_COVER_CLOSED: + case DVD_STATE_NO_DISK: + case DVD_STATE_COVER_OPEN: + case DVD_STATE_WRONG_DISK: + retVal = FALSE; + break; + case DVD_STATE_END: + case DVD_STATE_PAUSING: + coverReg = __DIRegs[1]; + if (((coverReg >> 2) & 1) || (coverReg & 1)) { + retVal = FALSE; + } else if (ResumeFromHere != 0) { + retVal = FALSE; + } else { + retVal = TRUE; + } + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +void __DVDPrepareResetAsync(DVDCBCallback callback) { + BOOL enabled; + + enabled = OSDisableInterrupts(); + + __DVDClearWaitingQueue(); + + if (Canceling) { + CancelCallback = callback; + } else { + if (executing) { + executing->callback = NULL; + } + + DVDCancelAllAsync(callback); + } + + OSRestoreInterrupts(enabled); +} + +static void Callback(s32 result, DVDCommandBlock* block ){ + Prepared = TRUE; +} + +void __DVDPrepareReset(void) { + OSDisableInterrupts(); + + Prepared = FALSE; + + __DVDPrepareResetAsync(Callback); + OSEnableInterrupts(); + + while (!(Prepared == TRUE)) { + + } +} + +int __DVDTestAlarm(const OSAlarm* alarm) { + if (alarm == &ResetAlarm) { + return 1; + } + return __DVDLowTestAlarm(alarm); +} + +BOOL __DVDLowBreak(void) { + Breaking = TRUE; + return TRUE; +} + +BOOL __DVDStopMotorAsync(void) { + return TRUE; +} + +void __DVDRestartMotor(void) {} diff --git a/src/revolution/dvd/dvdDeviceError.c b/src/revolution/dvd/dvdDeviceError.c new file mode 100644 index 0000000000..316346461f --- /dev/null +++ b/src/revolution/dvd/dvdDeviceError.c @@ -0,0 +1,200 @@ +#include +#include +#include + +#include "__os.h" +#include "__dvd.h" + +static u8 CheckBuffer[32] ATTRIBUTE_ALIGN(32); + +static volatile BOOL lowDone = TRUE; +static volatile u32 lowIntType = 0; + +static void lowCallback(u32 intType) { + lowIntType = intType; + lowDone = TRUE; +} + +void __DVDShowDeviceErrorMessage(void); + +BOOL __DVDCheckDevice(void) { + u32 checkCode = 0x460A0000; + u32 outOfRangeError = 0xFFFFFFFF; + u32 reportKeyError = 0xFFFFFFFF; + OSIOSRev iosRev; + + if (OSGetPhysicalMem2Size() == 0x08000000) { + return TRUE; + } + + __OSGetIOSRev(&iosRev); + + if (iosRev.major < 30 || iosRev.major >= 254) { + return TRUE; + } + + if (__OSDeviceCheckCode == 129) { + checkCode = 0x7ED40000; + } + + lowDone = FALSE; + DVDLowUnencryptedRead((void*)CheckBuffer, 32, checkCode, lowCallback);\ + + while (!lowDone) { + + } + + switch (lowIntType) { + case 2: + break; + case 1: + goto invalid; + break; + default: + goto fatal; + break; + } + + lowDone = FALSE; + DVDLowRequestError(lowCallback); + + while (!lowDone) { + + } + + outOfRangeError = DVDLowGetImmBufferReg(); + + switch (lowIntType) { + case 1: { + if ((DVDLowGetImmBufferReg() & 0xFF000000) != 0) { + goto recover; + break; + } + + switch (DVDLowGetImmBufferReg() & 0xFFFFFF) { + case 0x52100: + break; + default: + goto invalid; + break; + } + + break; + } + + default: + goto fatal; + break; + } + + lowDone = FALSE; + DVDLowReportKey((DVDVideoReportKey*)CheckBuffer, 0x40000, 0, lowCallback); + + while (!lowDone) { + + } + + switch (lowIntType) { + case 2: + break; + case 1: + goto invalid; + break; + default: + goto fatal; + break; + } + + lowDone = FALSE; + DVDLowRequestError(lowCallback); + + while (!lowDone) { + + } + + reportKeyError = DVDLowGetImmBufferReg(); + switch (lowIntType) { + case 1: + if ((DVDLowGetImmBufferReg() & 0xFF000000) != 0) { + goto recover; + break; + } + + switch (DVDLowGetImmBufferReg() & 0xFFFFFF) { + case 0x53100: + case 0x52000: + break; + default: + goto invalid; + break; + } + + break; + + default: + goto fatal; + break; + } + +valid: + return TRUE; + +invalid: + __DVDShowDeviceErrorMessage(); + return FALSE; + +recover: + return FALSE; + +fatal: + __DVDShowFatalMessage(); + return FALSE; +} + +const char* const __DVDDeviceErrorMessage[] = { + "\n\n\nエラーコード001。\n" + "不明なデバイスが見つかりました。", + + "\n\n\nError #001,\n" + "unauthorized device has been detected.", + + "\n\n\nFehler #001:\n" + "Es wurde eine unzul舖sige Komponente\n" + "entdeckt.", + + "\n\n\nErreur 001:\n" + "un dispositif non autoris\xE9\x20""a \xE9\x74\xE9\x20""d\xE9\x74""ect\xE9\x2E", + + "\n\n\nError 001:\n" + "Se ha detectado un dispositivo no\n" + "autorizado.", + + "\n\n\nErrore #001:\n" + "rilevato un dispositivo non autorizzato.", + + "\n\n\nFout #001:\n" + "ongeoorloofd onderdeel gevonden." +}; + +void __DVDShowDeviceErrorMessage(void) { + const char* message; + const char* const* messageList; + GXColor bg = { 0, 0, 0, 0 }; + GXColor fg = { 255, 255, 255, 0 }; + + if (SCGetLanguage() == 0) { + OSSetFontEncode(1); + } else { + OSSetFontEncode(0); + } + + messageList = __DVDDeviceErrorMessage; + + if (SCGetLanguage() > 6) { + message = messageList[1]; + } else { + message = messageList[SCGetLanguage()]; + } + + OSFatal(fg, bg, message); +} diff --git a/src/revolution/dvd/dvdFatal.c b/src/revolution/dvd/dvdFatal.c new file mode 100644 index 0000000000..4172ac10ac --- /dev/null +++ b/src/revolution/dvd/dvdFatal.c @@ -0,0 +1,211 @@ +#include +#include +#include + +#include "__dvd.h" + +static void (*FatalFunc)(); + +const char* const __DVDErrorMessageDefault[] = { + "\n" + "\n" + "\n" + "エラーが発生しました。\n" + "\n" + "イジェクトボタンを押してディスクを取り出してか\n" + "ら、本体の電源をOFFにして、本体の取扱説明書の\n" + "指示に従ってください。", + + "\n" + "\n" + "\n" + "An error has occurred.\n" + "Press the Eject Button, remove the\n" + "Game Disc, and turn off the power to the\n" + "console. Please read the Wii Operations\n" + "Manual for further instructions.", + + "\n" + "\n" + "\n" + "Ein Fehler ist aufgetreten.\n" + "Dr""\xFC""cke den Ausgabeknopf, entnimm die\n" + "Disc und schalte die Wii-Konsole aus.\n" + "Bitte lies die Wii-Bedienungsanleitung,\n" + "um weitere Informationen zu erhalten.", + + "\n" + "\n" + "\n" + "Une erreur est survenue.\n" + "Appuyez sur le bouton EJECT, retirez\n" + "le disque et ""\xE9""teignez la console.\n" + "Veuillez vous r""\xE9""f""\xE9""rer au Mode d'emploi\n" + "de la Wii pour plus de d""\xE9""tails.", + + "\n" + "\n" + "\n" + "Ocurri\xF3 un Error.\n" + "Oprime el Bot\xF3n EJECT, saca el disco\n" + "y apaga la consola. Consulta el manual\n" + "de operaciones de la consola Wii para\n" + "obtener m\xE1s informaci\xF3n.", + + "\n" + "\n" + "\n" + "Si \xE8 verificato un errore.\n" + "Premi il pulsante EJECT, estrai il disco\n" + "e spegni la console. Per maggiori\n" + "informazioni, consulta il manuale di\n" + "istruzioni della console Wii.", + + "\n" + "\n" + "\n" + "Er is een fout opgetreden.\n" + "Druk op de EJECT-knop, verwijder de\n" + "disk en zet het Wii-systeem uit. Lees\n" + "de Wii-handleiding voor meer informatie." +}; + +const char* const __DVDErrorMessageEurope[] = { + "\n" + "\n" + "\n" + "エラーが発生しました。\n" + "\n" + "イジェクトボタンを押してディスクを取り出してか\n" + "ら、本体の電源をOFFにして、本体の取扱説明書の\n" + "指示に従ってください。", + + "\n" + "\n" + "\n" + "An error has occurred.\n" + "Press the EJECT Button, remove the Disc,\n" + "and turn off the power to the console.\n" + "Please refer to the Wii Operations Manual\n" + "for details.", + + "\n" + "\n" + "\n" + "Ein Fehler ist aufgetreten.\n" + "Dr""\xFC""cke den Ausgabeknopf, entnimm die\n" + "Disc und schalte die Wii-Konsole aus.\n" + "Bitte lies die Wii-Bedienungsanleitung,\n" + "um weitere Informationen zu erhalten.", + + "\n" + "\n" + "\n" + "Une erreur est survenue.\n" + "Appuyez sur le bouton EJECT, retirez\n" + "le disque et ""\xE9""teignez la console.\n" + "Veuillez vous r""\xE9""f""\xE9""rer au mode d'emploi\n" + "Wii pour plus de d""\xE9""tails.", + + "\n" + "\n" + "\n" + "Se ha producido un error.\n" + "Pulsa el Bot\xF3n EJECT, extrae el disco y\n" + "apaga la consola. Consulta el manual de\n" + "instrucciones de la consola Wii para\n" + "obtener m\xE1s informaci\xF3n.", + + "\n" + "\n" + "\n" + "Si \xE8 verificato un errore.\n" + "Premi il pulsante EJECT, estrai il disco\n" + "e spegni la console. Per maggiori\n" + "informazioni, consulta il manuale di\n" + "istruzioni della console Wii.", + + "\n" + "\n" + "\n" + "Er is een fout opgetreden.\n" + "Druk op de EJECT-knop, verwijder de\n" + "disk en zet het Wii-systeem uit. Lees\n" + "de Wii-handleiding voor meer informatie." +}; + +const char* __DVDErrorMessageChinaKorea [] = { + "\n" + "\n" + "エラーコード104。\n" + "エラーが発生しました。\n" + "\n" + "イジェクトボタンを押してディスクを取り出してか\n" + "ら、本体の電源をOFFにして、本体の取扱説明書の\n" + "指示に従ってください。", + + "\n" + "\n" + "Error #104,\n" + "An error has occurred.\n" + "Press the EJECT Button, remove the\n" + "Game Disc, and turn the power off.\n" + "Please read the Wii Operations Manual\n" + "for more information." +}; + +void __DVDShowFatalMessage(void) { + const char* message; + const char* const* messageList; + GXColor bg = { 0, 0, 0, 0 }; + GXColor fg = { 255, 255, 255, 0 }; + + if (SCGetLanguage() == SC_LANG_JAPANESE) { + OSSetFontEncode(OS_FONT_ENCODE_SJIS); + } else { + OSSetFontEncode(OS_FONT_ENCODE_ANSI); + } + + switch (SCGetProductGameRegion()) { + case 0: + case 1: + case 3: + default: + messageList = __DVDErrorMessageDefault; + break; + case 2: + messageList = __DVDErrorMessageEurope; + break; + case 4: + case 5: + messageList = __DVDErrorMessageChinaKorea; + break; + } + + if (SCGetLanguage() > SC_LANG_DUTCH) { + message = messageList[1]; + } else { + message = messageList[SCGetLanguage()]; + } + + OSFatal(fg, bg, message); +} + +BOOL DVDSetAutoFatalMessaging(BOOL enable) { + BOOL enabled, prev; + enabled = OSDisableInterrupts(); + prev = FatalFunc ? TRUE : FALSE; + FatalFunc = enable ? __DVDShowFatalMessage : NULL; + OSRestoreInterrupts(enabled); + return prev; +} + +BOOL __DVDGetAutoFatalMessaging(void) { + return FatalFunc ? TRUE : FALSE; +} + +void __DVDPrintFatalMessage(void) { + if (FatalFunc) { + FatalFunc(); + } +} diff --git a/src/revolution/dvd/dvd_broadway.c b/src/revolution/dvd/dvd_broadway.c new file mode 100644 index 0000000000..ab4840791f --- /dev/null +++ b/src/revolution/dvd/dvd_broadway.c @@ -0,0 +1,978 @@ +#include +#include +#include + +IOSFd DiFD = -1; + +static volatile u8 requestInProgress = FALSE; +static u8 breakRequested; +static u8 callbackInProgress; + +static u32 registerBuf[8] ATTRIBUTE_ALIGN(32); +static u32 statusRegister[8] ATTRIBUTE_ALIGN(32); +static u32 controlRegister[8] ATTRIBUTE_ALIGN(32); +static s32 lastTicketError[8] ATTRIBUTE_ALIGN(32); + +static u32 readLength; +static u32 spinUpValue; + +static diRegVals_t diRegValCache ATTRIBUTE_ALIGN(32); + +static u8 DVDLowInitCalled = FALSE; + +typedef enum callbackType { + BOGUS_TYPE = 0, + TRANSACTION_CB, + COVER_CB, + COVER_REG_CB +} callbackType_t; + +static int freeCommandBuf = 0; +static diCommand_t* diCommand; +static char* pathBuf; + +typedef struct dvdContext { + DVDLowCallback callback; + callbackType_t callbackType; + u8 inUse; + u32 contextMagic; + u32 contextNum; + u32 pad[3]; +} dvdContext_t; + +static int freeDvdContext = 0; +static u8 dvdContextsInited = FALSE; +static dvdContext_t dvdContexts[4] ATTRIBUTE_ALIGN(32); +static IOSIoVector ioVec[10] ATTRIBUTE_ALIGN(32); + +static void* ddrAllocAligned32(const int size) { + void* low, *high; + + if ((size & 0x1F) != 0) { + OSReport("(ddrAllocAligned32) size is not a multiple of 32 !!! \n"); + return 0; + } + + low = IPCGetBufferLo(); + high = IPCGetBufferHi(); + + if (((u32)low & 0x1F) != 0) { + low = (void*)(((u32)low + 31) & 0x1F); + } + + if ((u32)low + size > (u32)high) { + OSReport("(ddrAllocAligned32) Not enough space to allocate %d bytes\n", size); + } + + IPCSetBufferLo((void*)((u32)low + size)); + return low; +} + +static BOOL allocateStructures(void) { + if ((diCommand = ddrAllocAligned32(sizeof(diCommand_t) * 4)) == 0) { + OSReport("Allocation of diCommand blocks failed\n"); + return FALSE; + } + + if ((pathBuf = ddrAllocAligned32(32)) == 0) { + OSReport("Allocation of pathBuf failed\n"); + return FALSE; + } + + return TRUE; +} + +static void initDvdContexts(void) { + { + u32 i; + + for (i = 0; i < 4; i++) { + dvdContexts[i].callback = 0; + dvdContexts[i].callbackType = 0; + dvdContexts[i].inUse = FALSE; + dvdContexts[i].contextMagic = 0xFEEBDAED; + dvdContexts[i].contextNum = i; + } + } + + freeDvdContext = 0; +} + +IOSError doTransactionCallback(IOSError ret, void* context) { + dvdContext_t* dvdContext = context; + + if (dvdContext->contextMagic != 0xFEEBDAED) { + OSReport("(doTransactionCallback) Error - context mangled!\n"); + dvdContext->contextMagic = 0xFEEBDAED; + goto out; + } + + requestInProgress = FALSE; + + if (dvdContext->callback != NULL) { + int callbackArg; + callbackInProgress = TRUE; + callbackArg = ret; + + if (breakRequested == TRUE) { + breakRequested = FALSE; + callbackArg |= 8; + } + + if (callbackArg & 1) { + readLength = 0; + } + + dvdContext->callback((u32)callbackArg); + callbackInProgress = FALSE; + } + +out: + dvdContext->inUse = FALSE; + return 0; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +IOSError doCoverCallback(IOSError err, void* context) { + OSReport("(doCoverCallback) Error - context mangled!\n"); + return 0; +} + +IOSError doPrepareCoverRegisterCallback(IOSError err, void* context) { + dvdContext_t* dvdContext; + requestInProgress = FALSE; + + diRegValCache.CoverRegVal = registerBuf[0]; + dvdContext = (dvdContext_t*)context; + + if (dvdContext->contextMagic != 0xFEEBDAED) { + OSReport("(doTransactionCallback) Error - context mangled!\n"); + dvdContext->contextMagic = 0xFEEBDAED; + } else { + IOSError ret; + if (dvdContext->callback != 0) { + callbackInProgress = TRUE; + ret = err; + + if (breakRequested == TRUE) { + breakRequested = FALSE; + ret |= 8; + } + + dvdContext->callback(ret); + callbackInProgress = FALSE; + } + } + + dvdContext->inUse = FALSE; + return 0; +} + +static inline void nextCommandBuf(int* bufNum) { + (*bufNum)++; + + if (*bufNum >= 4) { + *bufNum = 0; + } +} + +static inline dvdContext_t* newContext(const DVDLowCallback callback, const callbackType_t type) { + int returnIndex; + + if ((dvdContexts[freeDvdContext].inUse != 0) == (u32)1) { + OSReport("(newContext) ERROR: freeDvdContext.inUse (#%d) is true\n", freeDvdContext); + OSReport("(newContext) Now spinning in infinite loop\n"); + + while (1) { + + } + } + + if (dvdContexts[freeDvdContext].contextMagic != 0xFEEBDAED) { + OSReport("(newContext) Something overwrote the context magic - spinning \n"); + + while (1) { + + } + } + + dvdContexts[freeDvdContext].callback = callback; + dvdContexts[freeDvdContext].callbackType = type; + dvdContexts[freeDvdContext].inUse = TRUE; + returnIndex = freeDvdContext; + freeDvdContext++; + + if (freeDvdContext >= 4) { + freeDvdContext = 0; + } + + return(dvdContexts + returnIndex); +} + +BOOL DVDLowFinalize(void) { + IOSError ret = IOS_Close(DiFD); + + if (ret != IOS_ERROR_OK) { + OSReport("(DVDLowFinish) Error: IOS_Close failed\n"); + return FALSE; + } + + DVDLowInitCalled = FALSE; + return TRUE; +} + +BOOL DVDLowInit(void) { + IOSError retVal; + + if (DVDLowInitCalled == FALSE) { + DVDLowInitCalled = TRUE; + retVal = IPCCltInit(); + + if (retVal != IOS_ERROR_OK) { + OSReport("IPCCltInit returned error: %d\n", retVal); + return FALSE; + } + + if (allocateStructures() == FALSE) { + return FALSE; + } + + if (dvdContextsInited == FALSE) { + initDvdContexts(); + dvdContextsInited = TRUE; + } + } + + strncpy(pathBuf, "/dev/di", 32); + DiFD = IOS_Open(pathBuf, 0); + + if (DiFD >= 0) { + return TRUE; + } else { + switch (DiFD) { + case IOS_ERROR_NOEXISTS: + OSReport("(DVDLowInit) Error: IOS_Open failed - pathname '/dev/di' does not exist\n"); + return FALSE; + break; + case IOS_ERROR_ACCESS: + OSReport("(DVDLowInit) Error: IOS_Open failed - calling thread lacks permission\n"); + return FALSE; + break; + case IOS_ERROR_MAX: + OSReport("(DVDLowInit) Error: IOS_Open failed - connection limit has been reached\n"); + return FALSE; + break; + default: + OSReport("(DVDLowInit) IOS_Open failed, errorcode = %d\n", DiFD); + return FALSE; + break; + } + } +} + +BOOL DVDLowReadDiskID(DVDDiskID* diskID, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + + if (diskID == 0) { + OSReport("@@@@@@ WARNING - Calling DVDLowReadDiskId with NULL ptr\n"); + } + + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x70; + + rv = IOS_IoctlAsync(DiFD, 0x70, &diCommand[freeCommandBuf], sizeof(diCommand_t), diskID, sizeof(DVDDiskID), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowReadDiskID) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowOpenPartition(const u32 partitionWordOffset, const ESTicket* const eTicket, const u32 numCertBytes, const u8* const certificates, ESTitleMeta* tmd, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + if (eTicket != 0 && ((u32)eTicket & 0x1F)) { + OSReport("(DVDLowOpenPartition) eTicket memory is unaligned\n"); + return FALSE; + } + + if (certificates != 0 && ((u32)certificates & 0x1F)) { + OSReport("(DVDLowOpenPartition) certificates memory is unaligned\n"); + return FALSE; + } + + if (tmd != 0 && ((u32)tmd & 0x1F)) { + OSReport("(DVDLowOpenPartition) certificates memory is unaligned\n"); + return FALSE; + } + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x8B; + diCommand[freeCommandBuf].arg[0] = partitionWordOffset; + ioVec[0].base = (u8*)&diCommand[freeCommandBuf]; + ioVec[0].length = sizeof(diCommand_t); + + ioVec[1].base = (u8*)eTicket; + if (eTicket == 0) { + ioVec[1].length = 0; + } else { + ioVec[1].length = sizeof(ESTicket); + } + + ioVec[2].base = (u8*)certificates; + if (certificates == 0) { + ioVec[2].length = 0; + } else { + ioVec[2].length = numCertBytes; + } + + ioVec[3].base = (u8*)tmd; + ioVec[3].length = sizeof(ESTitleMeta); + + ioVec[4].base = (u8*)&lastTicketError[0]; + ioVec[4].length = sizeof(lastTicketError); + + rv = IOS_IoctlvAsync(DiFD, 0x8B, 3, 2, ioVec, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowOpenPartition) IOS_IoctlvAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowOpenPartitionWithTmdAndTicket() { + OSReport("(%s) eTicket memory is unaligned\n", __FUNCTION__); + OSReport("(%s) tmd parameter cannot be NULL\n", __FUNCTION__); + OSReport("(%s) tmd memory is unaligned\n", __FUNCTION__); + OSReport("(%s) eTicket parameter cannot be NULL\n", __FUNCTION__); + return TRUE; +} + +BOOL DVDLowOpenPartitionWithTmdAndTicketView(const u32 partitionWordOffset, const ESTicketView* const eTicketView, const u32 numTmdBytes, const ESTitleMeta* const tmd, const u32 numCertBytes, const u8* const certificates, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + if (certificates != 0 && ((u32)certificates & 0x1F)) { + return FALSE; + } + + if (tmd == 0) { + OSReport("(%s) tmd parameter cannot be NULL\n", __FUNCTION__); + return FALSE; + } else if (((u32)tmd & 0x1F)) { + OSReport("(%s) tmd memory is unaligned\n", __FUNCTION__); + return FALSE; + } + + if (eTicketView == 0) { + OSReport("(%s) eTicketView parameter cannot be NULL\n", __FUNCTION__); + return FALSE; + } else if (((u32)eTicketView & 0x1F)) { + OSReport("(%s) eTicketView memory is unaligned\n", __FUNCTION__); + return FALSE; + } + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x94; + diCommand[freeCommandBuf].arg[0] = partitionWordOffset; + ioVec[0].base = (u8*)&diCommand[freeCommandBuf]; + ioVec[0].length = sizeof(diCommand_t); + + ioVec[1].base = (u8*)eTicketView; + ioVec[1].length = sizeof(ESTicketView); + + ioVec[2].base = (u8*)tmd; + ioVec[2].length = numTmdBytes; + + ioVec[3].base = (u8*)certificates; + if (certificates == 0) { + ioVec[3].length = 0; + } else { + ioVec[3].length = numCertBytes; + } + + ioVec[4].base = (u8*)&lastTicketError[0]; + ioVec[4].length = sizeof(lastTicketError); + + rv = IOS_IoctlvAsync(DiFD, 0x94, 4, 1, ioVec, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowOpenPartition) IOS_IoctlvAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowGetNoDiscBufferSizes(const u32 partitionWordOffset, u32* numTmdBytes, u32* numCertBytes, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + if (numTmdBytes == 0 || numCertBytes == 0) { + OSReport("(%s) Error: NULL pointer argument\n", __FUNCTION__); + return FALSE; + } + + if (((u32)numTmdBytes & 0x1F)) { + OSReport("(%s) numTmdBytes memory is unaligned\n", __FUNCTION__); + return FALSE; + } + + if (((u32)numCertBytes & 0x1F)) { + OSReport("(%s) certificates memory is unaligned\n", __FUNCTION__); + return FALSE; + } + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x92; + diCommand[freeCommandBuf].arg[0] = partitionWordOffset; + + ioVec[0].base = (u8*)&diCommand[freeCommandBuf]; + ioVec[0].length = sizeof(diCommand_t); + + ioVec[1].base = (u8*)numTmdBytes; + ioVec[1].length = 4; + + ioVec[2].base = (u8*)numCertBytes; + ioVec[2].length = 4; + + rv = IOS_IoctlvAsync(DiFD, 0x92, 1, 2, ioVec, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (%s) IOS_IoctlvAsync returned error: %d\n", __FUNCTION__, rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +#define is_aligned(addr) (((u32)(addr) & 0x1F) == 0) + +BOOL DVDLowGetNoDiscOpenPartitionParams(const u32 partitionWordOffset, ESTicket* eTicket, u32* numTmdBytes, ESTitleMeta* tmd, u32* numCertBytes, u8* certificates, u32* dataWordOffset, u8* h3HashPtr, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + if (eTicket == 0 || numTmdBytes == 0 || tmd == 0 || numCertBytes == 0 || certificates == 0 || dataWordOffset == 0 || h3HashPtr == 0) { + OSReport("(%s) Error: NULL pointer argument\n", __FUNCTION__); + return FALSE; + } + + if (!is_aligned(eTicket) || !is_aligned(numTmdBytes) || !is_aligned(tmd) || !is_aligned(numCertBytes) || !is_aligned(certificates) || !is_aligned(dataWordOffset) || !is_aligned(h3HashPtr)) { + OSReport("(%s) pointer argument is unaligned\n", __FUNCTION__); + return FALSE; + } + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x90; + diCommand[freeCommandBuf].arg[0] = partitionWordOffset; + + ioVec[0].base = (u8*)&diCommand[freeCommandBuf]; + ioVec[0].length = sizeof(diCommand_t); + + ioVec[1].base = (u8*)numTmdBytes; + ioVec[1].length = 4; + + ioVec[2].base = (u8*)numCertBytes; + ioVec[2].length = 4; + + ioVec[3].base = (u8*)eTicket; + ioVec[3].length = sizeof(ESTicket); + + ioVec[4].base = (u8 *) numTmdBytes; + ioVec[4].length = 4; + + ioVec[5].base = (u8*)tmd; + ioVec[5].length = *numTmdBytes; + + ioVec[6].base = (u8*)numCertBytes; + ioVec[6].length = 4; + + ioVec[7].base = certificates; + ioVec[7].length = *numCertBytes; + + ioVec[8].base = (u8*)dataWordOffset; + ioVec[8].length = 4; + + ioVec[9].base = h3HashPtr; + ioVec[9].length = 98304; + + rv = IOS_IoctlvAsync(DiFD, 0x90, 3, 7, ioVec, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (%s) IOS_IoctlvAsync returned error: %d\n", __FUNCTION__, rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowNoDiscOpenPartition() { + OSReport("DVDLowNoDiscOpenPartition"); + return TRUE; +} + +BOOL DVDLowClosePartition(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x8C; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + rv = IOS_IoctlAsync(DiFD, 0x8C, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowClosePartition) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowUnencryptedRead(void* destAddr, u32 length, u32 wordOffset, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + readLength = length; + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x8D; + diCommand[freeCommandBuf].arg[0] = length; + diCommand[freeCommandBuf].arg[1] = wordOffset; + + rv = IOS_IoctlAsync(DiFD, 0x8D, &diCommand[freeCommandBuf], sizeof(diCommand_t), destAddr, length, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowUnencryptedRead) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowStopMotor(u8 eject, u8 saving, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0xE3; + diCommand[freeCommandBuf].arg[0] = eject; + diCommand[freeCommandBuf].arg[1] = saving; + + rv = IOS_IoctlAsync(DiFD, 0xE3, &diCommand[freeCommandBuf], sizeof(diCommand_t), &diRegValCache, sizeof(diRegVals_t), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowStopMotor) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowWaitForCoverClose() { + OSReport("@@@ (DVDLowWaitForCoverClose) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +BOOL DVDLowInquiry(DVDDriveInfo* info, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + if (breakRequested == 0) {} + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x12; + rv = IOS_IoctlAsync(DiFD, 0x12, &diCommand[freeCommandBuf], sizeof(diCommand_t), info, sizeof(DVDDriveInfo), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowInquiry) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowRequestError(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0xE0; + rv = IOS_IoctlAsync(DiFD, 0xE0, &diCommand[freeCommandBuf], sizeof(diCommand_t), &diRegValCache, sizeof(diRegVals_t), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowRequestError) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowSetSpinupFlag(u32 spinUp) { + "(DVDLowSetSpinupFlag): Synch functions can't be called in callbacks\n"; // dead string + + spinUpValue = spinUp; + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowNotifyReset() { + OSReport("@@@ (DVDLowNotifyReset) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +BOOL DVDLowReset(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x8A; + diCommand[freeCommandBuf].arg[0] = spinUpValue; + rv = IOS_IoctlAsync(DiFD, 0x8A, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowReset) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowAudioBufferConfig(u8 enable, u32 size, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0xE4; + diCommand[freeCommandBuf].arg[0] = enable; + diCommand[freeCommandBuf].arg[1] = size; + rv = IOS_IoctlAsync(DiFD, 0xE4, &diCommand[freeCommandBuf], sizeof(diCommand_t), &diRegValCache, sizeof(diRegVals_t), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowAudioBufferConfig) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +u32 DVDLowGetCoverStatus() { + OSReport("(DVDLowGetCoverStatus): Synch functions can't be called in callbacks\n"); + OSReport("@@@ (DVDLowGetCoverStatus) IOS_Ioctl returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +u32 DVDLowReadDVD() { + OSReport("@@@ (DVDLowReadDVD) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowReadDVDConfig() { + OSReport("@@@ (DVDLowReadDVDConfig) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowReadDvdCopyright() { + OSReport("@@@ (DVDLowReadDvdCopyright) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowReadDvdPhysical() { + OSReport("@@@ (DVDLowReadDvdPhysical) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowReadDvdDiscKey() { + OSReport("@@@ (DVDLowReadDvdDiscKey) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +BOOL DVDLowReportKey(DVDVideoReportKey* reportKey, u32 format, u32 lsn, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0xA4; + diCommand[freeCommandBuf].arg[0] = format >> 16; + diCommand[freeCommandBuf].arg[1] = lsn; + + rv = IOS_IoctlAsync(DiFD, 0xA4, &diCommand[freeCommandBuf], sizeof(diCommand_t), reportKey, sizeof(DVDVideoReportKey), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowReportKey) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowOffset() { + OSReport("@@@ (DVDLowOffset) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowStopLaser() { + OSReport("@@@ (DVDLowStopLaser) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowReadDiskBca() { + OSReport("@@@ (DVDLowReadDiskBca) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowSerMeasControl() { + OSReport("@@@ (DVDLowSerMeasControl) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowRequestDiscStatus() { + OSReport("@@@ (DVDLowRequestDiscStatus) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowRequestRetryNumber() { + OSReport("@@@ (DVDLowRequestRetryNumber) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} + +BOOL DVDLowSetMaximumRotation(u32 subcmd, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0xDD; + diCommand[freeCommandBuf].arg[0] = (subcmd >> 16) & 3; + rv = IOS_IoctlAsync(DiFD, 0xDD, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowSetMaxRotation) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowRead(void* destAddr, u32 length, u32 wordOffset, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + if (((u32)destAddr & 0x1F) != 0) { + OSReport("(DVDLowRead): ERROR - destAddr buffer is not 32 byte aligned\n"); + return FALSE; + } + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + readLength = length; + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x71; + diCommand[freeCommandBuf].arg[0] = length; + diCommand[freeCommandBuf].arg[1] = wordOffset; + rv = IOS_IoctlAsync(DiFD, 0x71, &diCommand[freeCommandBuf], sizeof(diCommand_t), destAddr, length, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowRead) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowSeek(u32 wordOffset, DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0xAB; + diCommand[freeCommandBuf].arg[0] = wordOffset; + rv = IOS_IoctlAsync(DiFD, 0xAB, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowSeek) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowGetCoverReg() { + OSReport("(DVDLowGetCoverReg): Synch functions can't be called in callbacks\n"); + OSReport("@@@ (DVDLowGetCoverReg) IOS_Ioctl returned error: %d\n"); + return TRUE; +} + +u32 DVDLowGetCoverRegister(void) { + return diRegValCache.CoverRegVal; +} + +u32 DVDLowGetStatusRegister(void) { + return statusRegister[0]; +} + +u32 DVDLowGetControlRegister(void) { + return controlRegister[0]; +} + +BOOL DVDLowPrepareCoverRegister(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x7A; + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + rv = IOS_IoctlAsync(DiFD, 0x7A, &diCommand[freeCommandBuf], sizeof(diCommand_t), registerBuf, sizeof(registerBuf), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowPrepareCoverRegsiter) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowPrepareStatusRegister(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x95; + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + rv = IOS_IoctlAsync(DiFD, 0x95, &diCommand[freeCommandBuf], sizeof(diCommand_t), statusRegister, sizeof(statusRegister), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowPrepareStatusRegsiter) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL DVDLowPrepareControlRegister(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x96; + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + rv = IOS_IoctlAsync(DiFD, 0x96, &diCommand[freeCommandBuf], sizeof(diCommand_t), controlRegister, sizeof(controlRegister), doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowPrepareControlRegister) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +u32 DVDLowGetImmBufferReg(void) { + return diRegValCache.ImmRegVal; +} + +BOOL DVDLowUnmaskStatusInterrupts(void) { + return TRUE; +} + +BOOL DVDLowMaskCoverInterrupt(void) { + return TRUE; +} + +BOOL DVDLowClearCoverInterrupt(DVDLowCallback callback) { + dvdContext_t* dvdContext; + IOSError rv; + + nextCommandBuf(&freeCommandBuf); + diCommand[freeCommandBuf].theCommand = 0x86; + requestInProgress = TRUE; + dvdContext = newContext(callback, 1); + rv = IOS_IoctlAsync(DiFD, 0x86, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext); + + if (rv != IOS_ERROR_OK) { + OSReport("@@@ (DVDLowClearCoverInterrupt) IOS_IoctlAsync returned error: %d\n", rv); + dvdContext->inUse = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL __DVDLowTestAlarm(const OSAlarm *) { + return FALSE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +BOOL DVDLowEnableDvdVideo() { + OSReport("@@@ (DVDLowEnableDvdVideo) IOS_IoctlAsync returned error: %d\n"); + return TRUE; +} diff --git a/src/revolution/dvd/dvderror.c b/src/revolution/dvd/dvderror.c new file mode 100644 index 0000000000..7f1e8e52ad --- /dev/null +++ b/src/revolution/dvd/dvderror.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include "os/__os.h" +#include "__dvd.h" + +static BOOL ExistFlag = FALSE; +static NANDCommandBlock NandCb; +static NANDFileInfo NandInfo; +static DVDCBCallback Callback; +static u32 NextOffset; +DVDErrorInfo __ErrorInfo ATTRIBUTE_ALIGN(32); +DVDErrorInfo __FirstErrorInfo ATTRIBUTE_ALIGN(32); + +void cbForNandClose(s32 result, NANDCommandBlock* block) { + if (Callback) { + Callback((result == 0) ? 1 : 2, NULL); + } +} + +void cbForNandWrite(s32 result, NANDCommandBlock* block) { + if (NANDCloseAsync(&NandInfo, cbForNandClose, &NandCb) != 0) { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandSeek(s32 result, NANDCommandBlock* block) { + if (result == sizeof(DVDErrorInfo) * (1 + NextOffset)) { + if (NextOffset == 0) { + __ErrorInfo.nextOffset = (1 + NextOffset) % 7; + } + + DCFlushRange((void*)&__ErrorInfo, sizeof(__ErrorInfo)); + + if (NANDWriteAsync(&NandInfo, (void*)&__ErrorInfo, sizeof(__ErrorInfo), cbForNandWrite, &NandCb) != 0) { + cbForNandWrite(-1, NULL); + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandWrite0(s32 result, NANDCommandBlock* block) { + if (result == sizeof(__FirstErrorInfo)) { + if (NANDSeekAsync(&NandInfo, (s32)(sizeof(DVDErrorInfo) * (1 + NextOffset)), 0, cbForNandSeek, &NandCb) != 0) { + cbForNandSeek(-1, NULL); + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandSeek2(s32 result, NANDCommandBlock* block) { + if (result == sizeof(DVDErrorInfo)) { + __FirstErrorInfo.nextOffset = (__FirstErrorInfo.nextOffset + 1) % 7; + + if (NANDWriteAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandWrite0, &NandCb) != 0) { + cbForNandWrite0(-1, NULL); + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandRead(s32 result, NANDCommandBlock* block) { + if (result == sizeof(DVDErrorInfo)) { + NextOffset = __FirstErrorInfo.nextOffset; + + if (NANDSeekAsync(&NandInfo, sizeof(DVDErrorInfo), 0, cbForNandSeek2, &NandCb) != 0) { + cbForNandSeek2(-1, NULL); + } + } else { + __ErrorInfo.nextOffset = 1; + if (NANDWriteAsync(&NandInfo, (void*)&__ErrorInfo, sizeof(__ErrorInfo), cbForNandWrite, &NandCb) != 0) { + cbForNandWrite(-1, NULL); + } + } +} + +void cbForNandSeek0(s32 result, NANDCommandBlock* block) { + if (result == 0) { + NextOffset = 0; + __ErrorInfo.nextOffset = 1; + + if (NANDWriteAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandWrite0, &NandCb) != 0) { + cbForNandWrite0(-1, NULL); + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandSeek1(s32 result, NANDCommandBlock* block) { + if (result == sizeof(DVDErrorInfo)) { + if (NANDReadAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandRead, &NandCb) != 0) { + cbForNandRead(-1, NULL); + } + } else { + if (NANDSeekAsync(&NandInfo, 0, 0, cbForNandSeek0, &NandCb) != 0) { + cbForNandSeek0(-1, NULL); + } + } +} + +void cbForNandOpen(s32 result, NANDCommandBlock* block) { + if (result == 0) { + if (ExistFlag) { + if (NANDSeekAsync(&NandInfo, sizeof(DVDErrorInfo), 0, cbForNandSeek1, &NandCb) != 0) { + cbForNandSeek1(-1, NULL); + } + } else { + NextOffset = 0; + __ErrorInfo.nextOffset = 1; + if (NANDWriteAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandWrite0, &NandCb) != 0) { + cbForNandWrite0(-1, NULL); + } + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandCreate(s32 result, NANDCommandBlock* block) { + if (result == 0 || result == -6) { + if (result == -6) { + ExistFlag = TRUE; + } + + if (NANDPrivateOpenAsync("/shared2/test2/dvderror.dat", &NandInfo, 3, cbForNandOpen, &NandCb) != 0) { + if (Callback) { + Callback(2, NULL); + } + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForNandCreateDir(s32 result, NANDCommandBlock* block) { + if (result == 0 || result == -6) { + if (NANDPrivateCreateAsync("/shared2/test2/dvderror.dat", 0x3F, 0, cbForNandCreate, &NandCb) != 0) { + if (Callback) { + Callback(2, NULL); + } + } + } else { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForPrepareControlRegister(u32 intType) { + if (intType == 1) { + __ErrorInfo.unk_0x14 = DVDLowGetControlRegister(); + } else { + __ErrorInfo.unk_0x14 = 0xFFFFFFFF; + } + + if (NANDPrivateCreateDirAsync("/shared2/test2", 0x3F, 0, cbForNandCreateDir, &NandCb) != 0) { + if (Callback) { + Callback(2, NULL); + } + } +} + +void cbForPrepareStatusRegister(u32 intType) { + if (intType == 1) { + __ErrorInfo.status = DVDLowGetStatusRegister(); + } else { + __ErrorInfo.status = 0xFFFFFFFF; + } + + if (DVDLowPrepareControlRegister(cbForPrepareControlRegister) == 0) { + if (Callback) { + Callback(2, NULL); + } + } +} + +void __DVDStoreErrorCode(u32 error, DVDCBCallback callback) { + __ErrorInfo.error = error; + __ErrorInfo.dateTime = (u32)OSTicksToSeconds(OSGetTime()); + Callback = callback; + DVDLowPrepareStatusRegister(cbForPrepareStatusRegister); +} diff --git a/src/revolution/dvd/dvdfs.c b/src/revolution/dvd/dvdfs.c new file mode 100644 index 0000000000..5be8b8297c --- /dev/null +++ b/src/revolution/dvd/dvdfs.c @@ -0,0 +1,557 @@ +#include +#include + +#include "__dvd.h" + +typedef struct FSTEntry { + /* 0x00 */ unsigned int isDirAndStringOff; + /* 0x04 */ unsigned int parentOrPosition; + /* 0x08 */ unsigned int nextEntryOrLength; +} FSTEntry; + +static OSBootInfo* BootInfo; +static FSTEntry* FstStart; +static char* FstStringStart; +static u32 MaxEntryNum; +static u32 currentDirectory; + +OSThreadQueue __DVDThreadQueue; +u32 __DVDLongFileNameFlag = 1; + +// prototypes +static BOOL isSame(const char* path, const char* string); +static u32 myStrncpy(char* dest, char* src, u32 maxlen); +static u32 entryToPath(u32 entry, char* path, u32 maxlen); +static BOOL DVDConvertEntrynumToPath(s32 entrynum, char* path, u32 maxlen); +static void cbForReadAsync(s32 result, DVDCommandBlock* block); +static void cbForReadSync(s32 result, DVDCommandBlock* block); +static void cbForSeekAsync(s32 result, DVDCommandBlock* block); +static void cbForSeekSync(s32 result, DVDCommandBlock* block); +static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block); +static void cbForPrepareStreamSync(s32 result, DVDCommandBlock* block); + +void __DVDFSInit(void) { + BootInfo = (void*)OSPhysicalToCached(0); + FstStart = BootInfo->FSTLocation; + if (FstStart) { + MaxEntryNum = FstStart->nextEntryOrLength; + FstStringStart = (char*)FstStart + (MaxEntryNum* sizeof(FSTEntry)); + } +} + +/* For convenience */ +#define entryIsDir(i) (((FstStart[i].isDirAndStringOff & 0xff000000) == 0) ? FALSE : TRUE) +#define stringOff(i) (FstStart[i].isDirAndStringOff & ~0xff000000) +#define parentDir(i) (FstStart[i].parentOrPosition) +#define nextDir(i) (FstStart[i].nextEntryOrLength) +#define filePosition(i) (FstStart[i].parentOrPosition) +#define fileLength(i) (FstStart[i].nextEntryOrLength) + +static BOOL isSame(const char* path, const char* string) { + while (*string != '\0') { + if (tolower(*path++) != tolower(*string++)) { + return FALSE; + } + } + + if (*path == '/' || *path == '\0') { + return TRUE; + } + + return FALSE; +} + +s32 DVDConvertPathToEntrynum(const char* pathPtr) { + const char* ptr; + char* stringPtr; + BOOL isDir; + u32 length; + u32 dirLookAt; + u32 i; + const char* origPathPtr = pathPtr; + const char* extentionStart; + BOOL illegal; + BOOL extention; + + ASSERTMSGLINE(383, pathPtr, "DVDConvertPathToEntrynum(): null pointer is specified "); + + dirLookAt = currentDirectory; + + while (1) { + if (*pathPtr == '\0') { + return (s32)dirLookAt; + } else if (*pathPtr == '/') { + dirLookAt = 0; + pathPtr++; + continue; + } else if (*pathPtr == '.') { + if (*(pathPtr + 1) == '.') { + if (*(pathPtr + 2) == '/') { + dirLookAt = parentDir(dirLookAt); + pathPtr += 3; + continue; + } else if (*(pathPtr + 2) == '\0') { + return (s32)parentDir(dirLookAt); + } + } else if (*(pathPtr + 1) == '/') { + pathPtr += 2; + continue; + } else if (*(pathPtr + 1) == '\0') { + return (s32)dirLookAt; + } + } + + if (__DVDLongFileNameFlag == 0) { + extention = FALSE; + illegal = FALSE; + + for (ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++) { + if (*ptr == '.') { + if ((ptr - pathPtr > 8) || (extention == TRUE)) { + illegal = TRUE; + break; + } + extention = TRUE; + extentionStart = ptr + 1; + + } else if (*ptr == ' ') + illegal = TRUE; + } + + if ((extention == TRUE) && (ptr - extentionStart > 3)) + illegal = TRUE; + + if (illegal) + OSPanic(__FILE__, 452, + "DVDConvertEntrynumToPath(possibly DVDOpen or DVDChangeDir or DVDOpenDir): " + "specified directory or file (%s) doesn't match standard 8.3 format. This is a " + "temporary restriction and will be removed soon\n", + origPathPtr); + } else { + for (ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++) + ; + } + + isDir = (*ptr == '\0') ? FALSE : TRUE; + length = (u32)(ptr - pathPtr); + + ptr = pathPtr; + + for (i = dirLookAt + 1; i < nextDir(dirLookAt); i = entryIsDir(i) ? nextDir(i) : (i + 1)) { + if ((entryIsDir(i) == FALSE) && (isDir == TRUE)) { + continue; + } + + stringPtr = FstStringStart + stringOff(i); + + if (isSame(ptr, stringPtr) == TRUE) { + goto next_hier; + } + } + + return -1; + +next_hier: + if (!isDir) { + return (s32)i; + } + + dirLookAt = i; + pathPtr += length + 1; + } +} + +BOOL DVDFastOpen(s32 entrynum, DVDFileInfo* fileInfo) { + ASSERTMSGLINE(536, fileInfo, "DVDFastOpen(): null pointer is specified to file info address "); + ASSERTMSG1LINE(539, (entrynum >= 0) && ((u32) entrynum < (u32) MaxEntryNum), "DVDFastOpen(): specified entry number '%d' is out of range ", entrynum); + ASSERTMSG1LINE(542, !entryIsDir(entrynum), "DVDFastOpen(): entry number '%d' is assigned to a directory ", entrynum); + + if (entrynum < 0 || entrynum >= MaxEntryNum || entryIsDir(entrynum)) { + return FALSE; + } + + fileInfo->startAddr = filePosition(entrynum) >> __DVDLayoutFormat; + fileInfo->length = fileLength(entrynum); + fileInfo->callback = (DVDCallback)NULL; + fileInfo->cb.state = DVD_STATE_END; + + return TRUE; +} + +BOOL DVDOpen(const char* fileName, DVDFileInfo* fileInfo) { + s32 entry; + char currentDir[128]; + + ASSERTMSGLINE(572, fileName, "DVDOpen(): null pointer is specified to file name "); + ASSERTMSGLINE(573, fileInfo, "DVDOpen(): null pointer is specified to file info address "); + + entry = DVDConvertPathToEntrynum(fileName); + + if (0 > entry) { + DVDGetCurrentDir(currentDir, 128); + OSReport("Warning: DVDOpen(): file '%s' was not found under %s.\n", fileName, currentDir); + return FALSE; + } + + if (entryIsDir(entry)) { + ASSERTMSG1LINE(587, !entryIsDir(entry), "DVDOpen(): directory '%s' is specified as a filename ", fileName); + return FALSE; + } + + fileInfo->startAddr = filePosition(entry) >> __DVDLayoutFormat; + fileInfo->length = fileLength(entry); + fileInfo->callback = (DVDCallback)NULL; + fileInfo->cb.state = DVD_STATE_END; + + return TRUE; +} + +BOOL DVDClose(DVDFileInfo* fileInfo) { + ASSERTMSGLINE(611, fileInfo, "DVDClose(): null pointer is specified to file info address "); + DVDCancel(&(fileInfo->cb)); + return TRUE; +} + +static u32 myStrncpy(char* dest, char* src, u32 maxlen) { + u32 i = maxlen; + + while ((i > 0) && (*src != 0)) { + *dest++ = *src++; + i--; + } + + return (maxlen - i); +} + +static u32 entryToPath(u32 entry, char* path, u32 maxlen) { + u32 i; + char* name; + u32 loc; + + if (entry == 0) { + return 0; + } + + name = FstStringStart + stringOff(entry); + if (entryIsDir(entry)) { + i = parentDir(entry); + } else { + i = entry; + while (i != 0) { + if (entryIsDir(i) && nextDir(i) > entry) { + break; + } + + i--; + } + } + + loc = entryToPath(i, path, maxlen); + if (loc == maxlen) { + return loc; + } + + *(path + loc++) = '/'; + + loc += myStrncpy(path + loc, name, maxlen - loc); + return loc; +} + +static BOOL DVDConvertEntrynumToPath(s32 entrynum, char* path, u32 maxlen) { + u32 loc; + + ASSERTMSG1LINE(721, (entrynum >= 0) && (entrynum < MaxEntryNum), "DVDConvertEntrynumToPath: specified entrynum(%d) is out of range ", entrynum); + ASSERTMSG1LINE(723, maxlen > 1, "DVDConvertEntrynumToPath: maxlen should be more than 1 (%d is specified)", maxlen); + + loc = entryToPath((u32)entrynum, path, maxlen); + + if (loc == maxlen) { + path[maxlen - 1] = '\0'; + return FALSE; + } + + if (entryIsDir(entrynum)) { + if (loc == maxlen - 1) { + path[loc] = '\0'; + return FALSE; + } + + path[loc++] = '/'; + } + + path[loc] = '\0'; + return TRUE; +} + +BOOL DVDGetCurrentDir(char* path, u32 maxlen) { + ASSERTMSG1LINE(765, (maxlen > 1), "DVDGetCurrentDir: maxlen should be more than 1 (%d is specified)", maxlen); + return DVDConvertEntrynumToPath((s32)currentDirectory, path, maxlen); +} + +BOOL DVDChangeDir(const char* dirName) { + s32 entry; + char currentDir[128]; + + ASSERTMSGLINE(787, dirName, "DVDChangeDir(): null pointer is specified to directory name "); + entry = DVDConvertPathToEntrynum(dirName); + ASSERTMSG2LINE(795, entry >= 0, "DVDChangeDir(): directory '%s' is not found under %s ", dirName, (DVDGetCurrentDir(currentDir, 128), currentDir)); + ASSERTMSG1LINE(799, entryIsDir(entry), "DVDChangeDir(): file '%s' is specified as a directory name ", dirName); + + if (entry < 0 || entryIsDir(entry) == FALSE) { + return FALSE; + } + + currentDirectory = (u32)entry; + return TRUE; +} + +BOOL DVDReadAsyncPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset, DVDCallback callback, s32 prio) { + ASSERTMSGLINE(831, fileInfo, "DVDReadAsync(): null pointer is specified to file info address "); + ASSERTMSGLINE(832, addr, "DVDReadAsync(): null pointer is specified to addr "); + ASSERTMSGLINE(836, !OFFSET(addr, 32), "DVDReadAsync(): address must be aligned with 32 byte boundaries "); + ASSERTMSGLINE(838, !(length & 0x1F), "DVDReadAsync(): length must be multiple of 32 byte "); + ASSERTMSGLINE(840, !(offset & 3), "DVDReadAsync(): offset must be multiple of 4 byte "); + + DVD_ASSERTMSGLINE(845, (0 <= offset) && (offset <= fileInfo->length), "DVDReadAsync(): specified area is out of the file "); + + DVD_ASSERTMSGLINE(851, (0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE), "DVDReadAsync(): specified area is out of the file "); + + fileInfo->callback = callback; + DVDReadAbsAsyncPrio(&(fileInfo->cb), addr, length, (s32)(fileInfo->startAddr + (offset >> 2)), + cbForReadAsync, prio); + return TRUE; +} + +#ifndef offsetof +#define offsetof(type, memb) ((u32) & ((type*)0)->memb) +#endif + +static void cbForReadAsync(s32 result, DVDCommandBlock* block) { + DVDFileInfo* fileInfo; + + fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb)); + ASSERTLINE(869, (void*) &fileInfo->cb == (void*) block); + if (fileInfo->callback) { + (fileInfo->callback)(result, fileInfo); + } +} + +s32 DVDReadPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset, s32 prio) { + int result; + DVDCommandBlock* block; + s32 state; + BOOL enabled; + s32 retVal; + + ASSERTMSGLINE(901, fileInfo, "DVDRead(): null pointer is specified to file info address "); + ASSERTMSGLINE(902, addr, "DVDRead(): null pointer is specified to addr "); + ASSERTMSGLINE(906, !OFFSET(addr, 32), "DVDRead(): address must be aligned with 32 byte boundaries "); + ASSERTMSGLINE(908, !(length & 0x1F), "DVDRead(): length must be multiple of 32 byte "); + ASSERTMSGLINE(910, !(offset & 3), "DVDRead(): offset must be multiple of 4 byte "); + + DVD_ASSERTMSGLINE(915, (0 <= offset) && (offset <= fileInfo->length), "DVDRead(): specified area is out of the file "); + DVD_ASSERTMSGLINE(921, (0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE), "DVDRead(): specified area is out of the file "); + + block = &fileInfo->cb; + result = DVDReadAbsAsyncPrio(block, addr, length, fileInfo->startAddr + (offset >> 2), cbForReadSync, prio); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while (1) { + state = ((volatile DVDCommandBlock*)block)->state; + if (state == 0) { + retVal = (s32)block->transferredSize; + break; + } else if (state == -1) { + retVal = -1; + break; + } else if (state == 10) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForReadSync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDSeekAsyncPrio(DVDFileInfo* fileInfo, s32 offset, DVDCallback callback, s32 prio) { + ASSERTMSGLINE(898, fileInfo, "DVDSeek(): null pointer is specified to file info address "); + ASSERTMSGLINE(902, !(offset & 3), "DVDSeek(): offset must be multiple of 4 byte "); + + DVD_ASSERTMSGLINE(907, (0 <= offset) && (offset <= fileInfo->length), "DVDSeek(): offset is out of the file "); + + fileInfo->callback = callback; + DVDSeekAbsAsyncPrio(&fileInfo->cb, (u32)(char*)fileInfo->startAddr + offset, cbForSeekAsync, prio); + return 1; +} + +static void cbForSeekAsync(s32 result, DVDCommandBlock* block) { + DVDFileInfo* fileInfo; + + fileInfo = (DVDFileInfo *)&block->next; + ASSERTLINE(925, (void*) &fileInfo->cb == (void*) block); + if (fileInfo->callback) { + (fileInfo->callback)(result, fileInfo); + } +} + +s32 DVDSeekPrio(DVDFileInfo* fileInfo, s32 offset, s32 prio) { + int result; + DVDCommandBlock* block; + s32 state; + BOOL enabled; + s32 retVal; + + ASSERTMSGLINE(955, fileInfo, "DVDSeek(): null pointer is specified to file info address "); + ASSERTMSGLINE(959, !(offset & 3), "DVDSeek(): offset must be multiple of 4 byte "); + ASSERTMSGLINE(963, (offset >= 0) && ((u32) offset <= (u32) fileInfo->length), "DVDSeek(): offset is out of the file "); + + block = &fileInfo->cb; + result = DVDSeekAbsAsyncPrio(block, (u32)(char*)fileInfo->startAddr + offset, cbForSeekSync, prio); + if (!result) { + return -1; + } + enabled = OSDisableInterrupts(); + + while (1) { + state = ((volatile DVDCommandBlock*)block)->state; + if (state == 0) { + retVal = 0; + break; + } else if (state == -1) { + retVal = -1; + break; + } else if (state == 10) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForSeekSync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +s32 DVDGetFileInfoStatus(const DVDFileInfo* fileInfo) { + return DVDGetCommandBlockStatus(&fileInfo->cb); +} + +BOOL DVDFastOpenDir(s32 entrynum, DVDDir* dir) { + ASSERTMSGLINE(1048, dir, "DVDFastOpenDir(): null pointer is specified to dir structure address "); + ASSERTMSG1LINE(1051, entrynum >= 0 && entrynum < MaxEntryNum, "DVDFastOpenDir(): specified entry number \'%d\' is out of range ", entrynum); + ASSERTMSG1LINE(1054, entryIsDir(entrynum), "DVDFastOpenDir(): entry number \'%d\' is assigned to a file ", entrynum); + + if (entrynum < 0 || entrynum >= MaxEntryNum || !entryIsDir(entrynum)) { + return FALSE; + } + + dir->entryNum = entrynum; + dir->location = entrynum + 1; + dir->next = FstStart[entrynum].nextEntryOrLength; + return TRUE; +} + +BOOL DVDOpenDir(const char* dirName, DVDDir* dir) { + s32 entry; + char currentDir[128]; + + ASSERTMSGLINE(1178, dirName, "DVDOpendir(): null pointer is specified to directory name "); + ASSERTMSGLINE(1179, dir, "DVDOpenDir(): null pointer is specified to dir structure address "); + + entry = DVDConvertPathToEntrynum(dirName); + if (entry < 0) { + DVDGetCurrentDir(currentDir, sizeof(currentDir)); + OSReport("Warning: DVDOpenDir(): file \'%s\' was not found under %s.\n", dirName, currentDir); + return FALSE; + } + + if (!entryIsDir(entry)) { + ASSERTMSG1LINE(1193, entryIsDir(entry), "DVDOpendir(): file \'%s\' is specified as a directory name ", dirName); + return FALSE; + } + + dir->entryNum = entry; + dir->location = entry + 1; + dir->next = nextDir(entry); + return TRUE; +} + +int DVDReadDir(DVDDir* dir, DVDDirEntry* dirent) { + u32 loc; + + loc = dir->location; + if ((loc <= (u32) dir->entryNum) || ((u32) dir->next <= loc)) { + return 0; + } + dirent->entryNum = loc; + dirent->isDir = entryIsDir(loc); + dirent->name = FstStringStart + stringOff(loc); + dir->location = entryIsDir(loc) ? nextDir(loc) : loc + 1; + return 1; +} + +int DVDCloseDir(DVDDir* dir) { + return 1; +} + +void DVDRewindDir(DVDDir* dir) { + dir->location = dir->entryNum + 1; +} + +void* DVDGetFSTLocation(void) { + return BootInfo->FSTLocation; +} + +s32 DVDGetTransferredSize(DVDFileInfo* fileinfo) { + s32 bytes; + DVDCommandBlock* cb; + + cb = &(fileinfo->cb); + + switch (cb->state) { + case DVD_STATE_COVER_CLOSED: + case DVD_STATE_NO_DISK: + case DVD_STATE_COVER_OPEN: + case DVD_STATE_WRONG_DISK: + case DVD_STATE_FATAL_ERROR: + case DVD_STATE_MOTOR_STOPPED: + case DVD_STATE_CANCELED: + case DVD_STATE_RETRY: + case DVD_STATE_END: + bytes = (s32)cb->transferredSize; + break; + case DVD_STATE_WAITING: + bytes = 0; + break; + case DVD_STATE_BUSY: + bytes = (s32)(cb->transferredSize + (cb->currTransferSize - __DIRegs[6])); + break; + default: + ASSERTMSG1LINE(1391, FALSE, "DVDGetTransferredSize(): Illegal state (%d)", cb->state); + break; + } + + return bytes; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +void DVDGetEntrynum() { + OSReport("DVDGetEntrynum(): null pointer is specified to file info address "); +} + +// NOTE: function doesn't exist in TP debug, needed for string data +void DVDGetEntryName() { + OSReport("DVDGetEntryName: specified entrynum(%d) is out of range "); +} diff --git a/src/revolution/dvd/dvdidutils.c b/src/revolution/dvd/dvdidutils.c new file mode 100644 index 0000000000..2742086b58 --- /dev/null +++ b/src/revolution/dvd/dvdidutils.c @@ -0,0 +1,98 @@ +#include +#include +#include + +#include "__dvd.h" + +static u32 strnlen(const char* str, u32 maxlen) { + u32 i; + + for (i = 0; i < maxlen; i++) { + if (*str++ == 0) { + return i; + } + } + + return maxlen; +} + +int DVDCompareDiskID(const DVDDiskID* id1, const DVDDiskID* id2) { +#ifdef DEBUG + const char* game1; + const char* game2; + const char* company1; + const char* company2; + u8 diskNum1; + u8 diskNum2; + u8 version1; + u8 version2; + u32 length; + + ASSERTMSGLINE(70, id1, "DVDCompareDiskID(): Specified id1 is NULL\n"); + ASSERTMSGLINE(71, id2, "DVDCompareDiskID(): Specified id2 is NULL\n"); + + game1 = id1->gameName; + game2 = id2->gameName; + company1 = id1->company; + company2 = id2->company; + diskNum1 = id1->diskNumber; + diskNum2 = id2->diskNumber; + version1 = id1->gameVersion; + version2 = id2->gameVersion; + + length = strnlen(game1, sizeof(game1)); + ASSERTMSGLINE(84, length == 0 || length == 4, "DVDCompareDiskID(): Specified game name for id1 is neither NULL nor 4 character long\n"); + ASSERTMSGLINE(85, company1, "DVDCompareDiskID(): Specified company name for id1 is NULL\n"); + ASSERTMSGLINE(86, company1[1] != 0, "DVDCompareDiskID(): Specified company name for id1 is not 2 character long\n"); + ASSERTMSGLINE(87, diskNum1 == 0xFF || ((diskNum1 / 16) < 10 && diskNum1 % 16 < 10), "DVDCompareDiskID(): Specified disk number for id1 is neither 0xff nor a BCD number"); + ASSERTMSGLINE(88, version1 == 0xFF || ((version1 / 16) < 10 && version1 % 16 < 10), "DVDCompareDiskID(): Specified version number for id1 is neither 0xff nor a BCD number"); + + length = strnlen(game2, sizeof(game2)); + ASSERTMSGLINE(91, length == 0 || length == 4, "DVDCompareDiskID(): Specified game name for id2 is neither NULL nor 4 character long\n"); + ASSERTMSGLINE(92, company2, "DVDCompareDiskID(): Specified company name for id2 is NULL\n"); + ASSERTMSGLINE(93, company2[1] != 0, "DVDCompareDiskID(): Specified company name for id2 is not 2 character long\n"); + ASSERTMSGLINE(94, diskNum2 == 0xFF || ((diskNum2 / 16) < 10 && diskNum2 % 16 < 10), "DVDCompareDiskID(): Specified disk number for id2 is neither 0xff nor a BCD number"); + ASSERTMSGLINE(95, version2 == 0xFF || ((version2 / 16) < 10 && version2 % 16 < 10), "DVDCompareDiskID(): Specified version number for id2 is neither 0xff nor a BCD number"); +#endif + + if (id1->gameName[0] != 0 && id2->gameName[0] != 0 && strncmp(id1->gameName, id2->gameName, 4) != 0) { + return 0; + } + + if (id1->company[0] == 0 || id2->company[0] == 0 || strncmp(id1->company, id2->company, 2) != 0) { + return 0; + } + + if (id1->diskNumber != 0xFF && id2->diskNumber != 0xFF && id1->diskNumber != id2->diskNumber) { + return 0; + } + + if (id1->gameVersion != 0xFF && id2->gameVersion != 0xFF && id1->gameVersion != id2->gameVersion) { + return 0; + } + + return 1; +} + +DVDDiskID* DVDGenerateDiskID(DVDDiskID* id, const char* game, const char* company, u8 diskNum, u8 version) { + ASSERTMSGLINE(123, id, "DVDGenerateDiskID(): Specified id is NULL\n"); + ASSERTMSGLINE(124, game == NULL || strlen(game) == 4, "DVDGenerateDiskID(): Specified game name is neither NULL nor 4 character long\n"); + ASSERTMSGLINE(125, company, "DVDGenerateDiskID(): Specified company name is NULL\n"); + ASSERTMSGLINE(126, strlen(company) == 2, "DVDGenerateDiskID(): Specified company name is not 2 character long\n"); + ASSERTMSGLINE(127, diskNum == 0xFF || ((diskNum / 16) < 10 && diskNum % 16 < 10), "DVDGenerateDiskID(): Specified disk number is neither 0xff nor a BCD number"); + ASSERTMSGLINE(128, version == 0xFF || ((version / 16) < 10 && version % 16 < 10), "DVDGenerateDiskID(): Specified version number is neither 0xff nor a BCD number"); + + memset(id, 0, sizeof(DVDDiskID)); + + if (game != NULL) { + strncpy(id->gameName, game, 4); + } + + if (company != NULL) { + strncpy(id->company, company, 2); + } + + id->diskNumber = diskNum; + id->gameVersion = version; + return id; +} diff --git a/src/revolution/dvd/dvdqueue.c b/src/revolution/dvd/dvdqueue.c new file mode 100644 index 0000000000..aa60da0d7a --- /dev/null +++ b/src/revolution/dvd/dvdqueue.c @@ -0,0 +1,192 @@ +#include +#include + +#include "__dvd.h" + +static struct { + /* 0x00 */ DVDCommandBlock* next; + /* 0x04 */ DVDCommandBlock* prev; +} WaitingQueue[4]; + +// prototypes +static DVDCommandBlock* PopWaitingQueuePrio(s32 prio); + +void __DVDClearWaitingQueue(void) { + u32 i; + + for(i = 0; i < 4; i++) { + DVDCommandBlock* q = (DVDCommandBlock*)&WaitingQueue[i].next; + q->next = q; + q->prev = q; + } +} + +int __DVDPushWaitingQueue(s32 prio, DVDCommandBlock* block) { + BOOL enabled = OSDisableInterrupts(); + DVDCommandBlock* q = (DVDCommandBlock*)&WaitingQueue[prio]; + + q->prev->next = block; + block->prev = q->prev; + block->next = q; + q->prev = block; + OSRestoreInterrupts(enabled); + return 1; +} + +static DVDCommandBlock* PopWaitingQueuePrio(s32 prio) { + DVDCommandBlock* tmp; + BOOL enabled; + DVDCommandBlock* q; + + enabled = OSDisableInterrupts(); + q = (DVDCommandBlock*)&WaitingQueue[prio]; + ASSERTLINE(99, q->next != q); + + tmp = q->next; + q->next = tmp->next; + tmp->next->prev = q; + OSRestoreInterrupts(enabled); + tmp->next = 0; + tmp->prev = 0; + return tmp; +} + +DVDCommandBlock* __DVDPopWaitingQueue(void) { + u32 i; + BOOL enabled; + DVDCommandBlock* q; + + enabled = OSDisableInterrupts(); + for (i = 0; i < 4; i++) { + q = (DVDCommandBlock*)&WaitingQueue[i]; + if (q->next != q) { + OSRestoreInterrupts(enabled); + return PopWaitingQueuePrio(i); + } + } + + OSRestoreInterrupts(enabled); + return NULL; +} + +int __DVDCheckWaitingQueue(void) { + u32 i; + BOOL enabled; + DVDCommandBlock* q; + + enabled = OSDisableInterrupts(); + for (i = 0; i < 4; i++) { + q = (DVDCommandBlock*)&WaitingQueue[i]; + if (q->next != q) { + OSRestoreInterrupts(enabled); + return 1; + } + } + + OSRestoreInterrupts(enabled); + return 0; +} + +DVDCommandBlock* __DVDGetNextWaitingQueue(void) { + u32 i; + BOOL enabled; + DVDCommandBlock* q, *tmp; + + enabled = OSDisableInterrupts(); + + for (i = 0; i < 4; i++) { + q = (DVDCommandBlock*)&(WaitingQueue[i]); + + if (q->next != q) { + tmp = q->next; + OSRestoreInterrupts(enabled); + return tmp; + } + } + + OSRestoreInterrupts(enabled); + return NULL; +} + +int __DVDDequeueWaitingQueue(DVDCommandBlock* block) { + BOOL enabled; + DVDCommandBlock* prev; + DVDCommandBlock* next; + + enabled = OSDisableInterrupts(); + prev = block->prev; + next = block->next; + if (prev == NULL || next == NULL) { + OSRestoreInterrupts(enabled); + return 0; + } + prev->next = next; + next->prev = prev; + OSRestoreInterrupts(enabled); + return 1; +} + +int __DVDIsBlockInWaitingQueue(DVDCommandBlock* block) { + u32 i; + DVDCommandBlock* start; + DVDCommandBlock* q; + + for (i = 0; i < 4; i++) { + start = (DVDCommandBlock*)&WaitingQueue[i]; + if (start->next == start) { + continue; + } + + for (q = start->next; q != start; q = q->next) { + if (q == block) { + return 1; + } + } + } + + return 0; +} + +static char* CommandNames[16] = { + "", + "READ", + "SEEK", + "CHANGE_DISK", + "BSREAD", + "READID", + "INITSTREAM", + "CANCELSTREAM", + "STOP_STREAM_AT_END", + "REQUEST_AUDIO_ERROR", + "REQUEST_PLAY_ADDR", + "REQUEST_START_ADDR", + "REQUEST_LENGTH", + "AUDIO_BUFFER_CONFIG", + "INQUIRY", + "BS_CHANGE_DISK", +}; + +void DVDDumpWaitingQueue(void) { + u32 i; + DVDCommandBlock* start; + DVDCommandBlock* q; + + OSReport("==== DVD Waiting Queue Status ====\n"); + for (i = 0; i < 4; i++) { + OSReport("< Queue #%d > ", i); + start = (DVDCommandBlock*)&WaitingQueue[i]; + if (start->next == start) { + OSReport("None\n"); + } else { + OSReport("\n"); + for (q = start->next; q != start; q = q->next) { + OSReport("0x%08x: Command: %s ", q, CommandNames[q->command]); + if (q->command == 1) { + OSReport("Disk offset: %d, Length: %d, Addr: 0x%08x\n", q->offset, q->length, q->addr); + } else { + OSReport("\n"); + } + } + } + } +} diff --git a/src/revolution/fs/fs.c b/src/revolution/fs/fs.c new file mode 100644 index 0000000000..5ea6eecd54 --- /dev/null +++ b/src/revolution/fs/fs.c @@ -0,0 +1,973 @@ +#include +#include +#include + +static IOSFd __fsFd = -1; +static u32 __fsInitialized = FALSE; +static char* __devfs = 0; +static IOSHeapId hId; +static s32 _asynCnt = 0; + +#define ROUNDUP(sz) (((u32)(sz) + 31) & ~(u32)(31)) + +typedef struct isfs_GetAttr { + IOSUid* ownerId; + IOSGid* groupId; + u32* attr; + u32* ownerAcc; + u32* groupAcc; + u32* othersAcc; +} isfs_GetAttr; + +typedef struct isfs_GetUsage { + u32* nblocks; + u32* ninodes; +} isfs_GetUsage; + +typedef struct __isfsCtxt { + u8 ioBuf[ROUNDUP(256)] ATTRIBUTE_ALIGN(32); + ISFSCallback cb; + void* ctxt; + u32 func; + + union { + ISFSStats* stats; + ISFSFileStats* fstats; + u32* num; + isfs_GetAttr ga; + isfs_GetUsage gu; + } args; +} __isfsCtxt; + +static IOSError _FSGetStatsCb(IOSError ret, void* ctxt); +static IOSError _FSReadDirCb(IOSError ret, void* ctxt); +static IOSError _FSGetAttrCb(IOSError ret, void* ctxt); +static IOSError _FSGetUsageCb(IOSError ret, void* ctxt); +static IOSError _FSGetFileStatsCb(IOSError ret, void* ctxt); + +ISFSError ISFS_OpenLib(void) { + ISFSError rc = 0; + static void* lo = 0, *hi = 0; + __isfsCtxt* __fsCtxt = 0; + + if (!__fsInitialized) { + lo = IPCGetBufferLo(); + hi = IPCGetBufferHi(); + } + + __devfs = (char*)ROUNDUP(lo); + if (!__fsInitialized && ((u32)__devfs + ROUNDUP(64)) > (u32)hi) { + OSReport("APP ERROR: Not enough IPC arena\n"); + rc = -22; + goto out; + } + + strcpy(__devfs, "/dev/fs"); + __fsFd = IOS_Open(__devfs, 0); + + if (__fsFd < 0) { + rc = __fsFd; + goto out; + } + + __fsCtxt = (__isfsCtxt*)((u32)__devfs); + + if (!__fsInitialized && ((u32)__fsCtxt + (16 + 1) * ROUNDUP(sizeof(__isfsCtxt))) > (u32)hi) { + OSReport("APP ERROR: Not enough IPC arena\n"); + rc = -22; + goto out; + } + + if (!__fsInitialized) { + IPCSetBufferLo((void*)((u32)__fsCtxt + (17) * ROUNDUP(sizeof(__isfsCtxt)))); + __fsInitialized = TRUE; + } + + hId = iosCreateHeap(__fsCtxt, 17 * ROUNDUP(sizeof(__isfsCtxt))); + + if (hId < 0) { + rc = -22; + goto out; + } + +out: + return rc; +} + +IOSError _isfsFuncCb(IOSError ret, void* ctxt) { + ISFSError rc = 0; + __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt; + rc = ret; + + if (rc >= 0) { + switch (_ctxt->func) { + case 1: + _FSGetStatsCb(ret, ctxt); + break; + case 2: + _FSReadDirCb(ret, ctxt); + break; + case 3: + _FSGetAttrCb(ret, ctxt); + break; + case 4: + _FSGetUsageCb(ret, ctxt); + break; + case 5: + _FSGetFileStatsCb(ret, ctxt); + break; + default: + break; + } + } + + _asynCnt = 0; + + if (_ctxt->cb) { + _ctxt->cb(rc, _ctxt->ctxt); + } + + if (ctxt) { + iosFree(hId, ctxt); + } + + return rc; +} + +static IOSError _FSGetStatsCb(IOSError ret, void* ctxt) { + __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt; + ISFSError rc = ISFS_ERROR_OK; + + if (ret == 0) { + memcpy(_ctxt->args.stats, _ctxt->ioBuf, sizeof(*_ctxt->args.stats)); + } + + return rc; +} + +s32 ISFS_CreateDir(const u8* dname, u32 dirAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc) { + ISFSError rc = ISFS_ERROR_OK; + ISFSPathAttrArgs* pathAttrArgs; + __isfsCtxt* blCtxt = 0; + u32 len; + + if (dname == NULL || __fsFd < 0 || (len = strnlen(dname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + pathAttrArgs = (ISFSPathAttrArgs*)blCtxt->ioBuf; + memcpy(pathAttrArgs->path, dname, len + 1); + + pathAttrArgs->attr = (u8)dirAttr; + pathAttrArgs->ownerAccess = (u8)ownerAcc; + pathAttrArgs->groupAccess = (u8)groupAcc; + pathAttrArgs->othersAccess = (u8)othersAcc; + rc = IOS_Ioctl(__fsFd, 3, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0); + +out: + /* they seem to have had a macro for this, and the programmer forgot it did a NULL check */ + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + + } + + return rc; +} + +s32 ISFS_CreateDirAsync(const u8* dname, u32 dirAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + u32 len; + ISFSPathAttrArgs* pathAttrArgs; + __isfsCtxt* ctxt; + + if (dname == NULL || __fsFd < 0 || (len = strnlen(dname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + pathAttrArgs = (ISFSPathAttrArgs*)ctxt->ioBuf; + memcpy(pathAttrArgs->path, (void*)dname, len + 1); + pathAttrArgs->attr = dirAttr; + pathAttrArgs->ownerAccess = ownerAcc; + pathAttrArgs->groupAccess = groupAcc; + pathAttrArgs->othersAccess = othersAcc; + rc = IOS_IoctlAsync(__fsFd, 3, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_ReadDir(const u8* dname, u8* nameList, u32* num) { + ISFSError rc = ISFS_ERROR_OK; + IOSIoVector* v = 0; + u32 len, numInputs, numOutputs, *numPtr; + char* dnPtr; + __isfsCtxt* blCtxt = 0; + + if (dname == NULL || num == NULL || __fsFd < 0 || (u32)nameList & 31 || (len = strnlen(dname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + v = (IOSIoVector*)blCtxt->ioBuf; + dnPtr = (char*)ROUNDUP((u8*)&v[4]); + memcpy(dnPtr, dname, len + 1); + v[0].base = (u8*)dnPtr; + v[0].length = 64; + numPtr = (u32*)ROUNDUP((u32)dnPtr + 64); + v[1].base = (u8*)numPtr; + v[1].length = 4; + + if (nameList != 0) { + numInputs = 2; + numOutputs = 2; + *numPtr = *num; + v[2].base = nameList; + v[2].length = *num * 13; + v[3].base = (u8*)numPtr; + v[3].length = 4; + } else { + numInputs = 1; + numOutputs = 1; + } + + rc = IOS_Ioctlv(__fsFd, 4, numInputs, numOutputs, v); + + if (rc != ISFS_ERROR_OK) { + goto out; + } + + *num = *numPtr; + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +static IOSError _FSReadDirCb(IOSError ret, void* ctxt) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt; + + if (ret == 0) { + u8* ptr; + IOSIoVector* v = (IOSIoVector*)_ctxt->ioBuf; + ptr = (u8*)ROUNDUP((u8*)&v[4]); + ptr = (u8*)ROUNDUP(ptr + 64); + *_ctxt->args.num = *(u32 *) ptr; + } + + return rc; +} + +s32 ISFS_ReadDirAsync(const u8* dname, u8* nameList, u32* num, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + u32 len, numOutputs, numInputs, *numPtr; + IOSIoVector* v; + __isfsCtxt* ctxt; + char* dnPtr; + + if (dname == NULL || num == NULL || __fsFd < 0 || (u32)nameList & 31 || (len = strnlen(dname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 2; + ctxt->args.num = num; + v = (IOSIoVector*)ctxt->ioBuf; + dnPtr = (char*)ROUNDUP((u8*)&v[4]); + memcpy(dnPtr, dname, len + 1); + v[0].base = (u8*)dnPtr; + v[0].length = 64; + numPtr = (u32*)ROUNDUP((u32)dnPtr + 64); + v[1].base = (u8*)numPtr; + v[1].length = 4; + + if (nameList != 0) { + numInputs = 2; + numOutputs = 2; + *numPtr = *num; + v[2].base = nameList; + v[2].length = *num * 13; + v[3].base = (u8*)numPtr; + v[3].length = 4; + } else { + numInputs = 1; + numOutputs = 1; + } + + rc = IOS_IoctlvAsync(__fsFd, 4, numInputs, numOutputs, v, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_GetAttr(const u8* name, IOSUid* ownerId, IOSGid* groupId, u32* attr, u32* ownerAcc, u32* groupAcc, u32* othersAcc) { + ISFSError rc = ISFS_ERROR_OK; + ISFSPathAttrArgs* pathAttrArgs; + u8* ptr; + u32 len; + __isfsCtxt* blCtxt = 0; + + if (name == NULL || __fsFd < 0 || + (len = strnlen(name, 64)) == 64 || + ownerId == NULL || groupId == NULL || attr == NULL || + ownerAcc == NULL || groupAcc == NULL || othersAcc == NULL) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + ptr = (u8*)blCtxt->ioBuf; + memcpy(ptr, name, len + 1); + pathAttrArgs = (ISFSPathAttrArgs*)ROUNDUP(ptr + 64); + rc = IOS_Ioctl(__fsFd, 6, ptr, 64, pathAttrArgs, sizeof(*pathAttrArgs)); + + if (rc != IOS_ERROR_OK) { + goto out; + } + + *ownerId = pathAttrArgs->ownerId; + *groupId = pathAttrArgs->groupId; + *attr = pathAttrArgs->attr; + *ownerAcc = pathAttrArgs->ownerAccess; + *groupAcc = pathAttrArgs->groupAccess; + *othersAcc = pathAttrArgs->othersAccess; + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +static IOSError _FSGetAttrCb(IOSError ret, void* ctxt) { + ISFSError rc = ret; + + if (ret == 0) { + __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt; + ISFSPathAttrArgs* pathAttrArgs = (ISFSPathAttrArgs*)ROUNDUP(_ctxt->ioBuf + 64); + + *_ctxt->args.ga.ownerId = pathAttrArgs->ownerId; + *_ctxt->args.ga.groupId = pathAttrArgs->groupId; + *_ctxt->args.ga.attr = pathAttrArgs->attr; + *_ctxt->args.ga.ownerAcc = pathAttrArgs->ownerAccess; + *_ctxt->args.ga.groupAcc = pathAttrArgs->groupAccess; + *_ctxt->args.ga.othersAcc = pathAttrArgs->othersAccess; + } + + return rc; +} + +s32 ISFS_GetAttrAsync(const u8* name, IOSUid* ownerId, IOSGid* groupId, u32* attr, u32* ownerAcc, u32* groupAcc, u32* othersAcc, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt *ctxt; + ISFSPathAttrArgs *pathAttrArgs; + u8 *ptr; + u32 len; + + if (name == NULL || __fsFd < 0 || + (len = strnlen(name, 64)) == 64 || + ownerId == NULL || groupId == NULL || attr == NULL || + ownerAcc == NULL || groupAcc == NULL || othersAcc == NULL) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->args.ga.ownerId = ownerId; + ctxt->args.ga.groupId = groupId; + ctxt->args.ga.attr = attr; + ctxt->args.ga.ownerAcc = ownerAcc; + ctxt->args.ga.groupAcc = groupAcc; + ctxt->args.ga.othersAcc = othersAcc; + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 3; + + ptr = (u8*)ctxt->ioBuf; + memcpy(ptr, name, len + 1); + pathAttrArgs = (ISFSPathAttrArgs*)ROUNDUP(ptr + 64); + + rc = IOS_IoctlAsync(__fsFd, 6, ptr, 64, pathAttrArgs, sizeof(*pathAttrArgs), _isfsFuncCb, ctxt); +out: + return rc; +} + +s32 ISFS_Delete(const u8* name) { + ISFSError rc = ISFS_ERROR_OK; + u32 len; + __isfsCtxt* blCtxt = 0; + + if (name == NULL || __fsFd < 0 || (len = strnlen(name, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + memcpy(blCtxt->ioBuf, name, len + 1); + rc = IOS_Ioctl(__fsFd, 7, blCtxt->ioBuf, 64, NULL, 0); + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +s32 ISFS_DeleteAsync(const u8* name, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + u32 len; + __isfsCtxt* ctxt; + + if (name == NULL || __fsFd < 0 || (len = strnlen(name, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + memcpy(ctxt->ioBuf, name, len + 1); + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + rc = IOS_IoctlAsync(__fsFd, 7, ctxt->ioBuf, 64, NULL, 0, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_Rename(const u8* oldName, const u8* newName) { + ISFSError rc = ISFS_ERROR_OK; + ISFSPathsArgs* pathsArgs; + u32 oldLen, newLen; + __isfsCtxt* blCtxt = 0; + + if (oldName == NULL || newName == NULL || __fsFd < 0 || + (oldLen = strnlen(oldName, 64)) == 64 || + (newLen = strnlen(newName, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + pathsArgs = (ISFSPathsArgs*)blCtxt->ioBuf; + memcpy(pathsArgs->path1, oldName, oldLen + 1); + memcpy(pathsArgs->path2, newName, newLen + 1); + rc = IOS_Ioctl(__fsFd, 8, pathsArgs, sizeof(*pathsArgs), NULL, 0); + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +s32 ISFS_RenameAsync(const u8* oldName, const u8* newName, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + ISFSPathsArgs* pathsArgs; + u32 oldLen, newLen; + __isfsCtxt* ctxt; + + if ((oldName == NULL) || newName == NULL || __fsFd < 0 || + (oldLen = strnlen(oldName, 64)) == 64 || + (newLen = strnlen(newName, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == NULL) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + + pathsArgs = (ISFSPathsArgs*)ctxt->ioBuf; + memcpy(pathsArgs->path1, oldName, oldLen + 1); + memcpy(pathsArgs->path2, newName, newLen + 1); + rc = IOS_IoctlAsync(__fsFd, 8, pathsArgs, sizeof(*pathsArgs), NULL, 0, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_GetUsage(const u8* dname, u32* nblocks, u32* ninodes) { + ISFSError rc = ISFS_ERROR_OK; + IOSIoVector* v = 0; + u32 len, *blkPtr, *inodePtr; + char* dnPtr; + __isfsCtxt* blCtxt = 0; + + if (dname == NULL || __fsFd < 0 || + nblocks == NULL || ninodes == NULL || + (len = strnlen(dname, 64)) == 64) { + + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + v = (IOSIoVector*)blCtxt->ioBuf; + dnPtr = (char*)ROUNDUP((u8*)&v[3]); + memcpy(dnPtr, dname, len + 1); + v[0].base = (u8*)dnPtr; + v[0].length = 64; + + blkPtr = (u32*) ROUNDUP(((u32)dnPtr) + 64); + inodePtr = (u32*) ROUNDUP(((u32)blkPtr) + 4); + v[1].base = (u8*) blkPtr; + v[1].length = 4; + v[2].base = (u8*)inodePtr; + v[2].length = 4; + + rc = IOS_Ioctlv(__fsFd, 12, 1, 2, v); + if (rc != ISFS_ERROR_OK) { + goto out; + } + + *nblocks = *blkPtr; + *ninodes = *inodePtr; + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +static IOSError _FSGetUsageCb(IOSError ret, void* ctxt) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt; + + if (ret == 0) { + u8* ptr; + IOSIoVector* v = (IOSIoVector*)_ctxt->ioBuf; + ptr = (u8*)ROUNDUP((u8*)&v[4]); + ptr = (u8*)ROUNDUP(ptr + 64); + *_ctxt->args.gu.nblocks = *(u32*)ptr; + ptr = (u8*)ROUNDUP(ptr + 4); + *_ctxt->args.gu.ninodes = *(u32*)ptr; + } + + return rc; +} + +s32 ISFS_CreateFile(const u8* fname, u32 fileAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc) { + ISFSError rc = ISFS_ERROR_OK; + ISFSPathAttrArgs* pathAttrArgs; + u32 len; + __isfsCtxt* blCtxt = NULL; + + if (fname == NULL || __fsFd < 0 || (len = strnlen(fname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + pathAttrArgs = (ISFSPathAttrArgs*)blCtxt->ioBuf; + memcpy(pathAttrArgs->path, fname, len + 1); + pathAttrArgs->attr = fileAttr; + pathAttrArgs->ownerAccess = ownerAcc; + pathAttrArgs->groupAccess = groupAcc; + pathAttrArgs->othersAccess = othersAcc; + rc = IOS_Ioctl(__fsFd, 9, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0); + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +s32 ISFS_CreateFileAsync(const u8* fname, u32 fileAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + ISFSPathAttrArgs* pathAttrArgs; + u32 len; + __isfsCtxt* ctxt; + + if (fname == NULL || __fsFd < 0 || (len = strnlen(fname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + + pathAttrArgs = (ISFSPathAttrArgs*)ctxt->ioBuf; + memcpy(pathAttrArgs->path, fname, len + 1); + pathAttrArgs->attr = fileAttr; + pathAttrArgs->ownerAccess = ownerAcc; + pathAttrArgs->groupAccess = groupAcc; + pathAttrArgs->othersAccess = othersAcc; + rc = IOS_IoctlAsync(__fsFd, 9, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0, _isfsFuncCb, ctxt); + +out: + return rc; +} + +IOSFd ISFS_Open(const u8* fname, u32 access) { + ISFSError rc = ISFS_ERROR_OK; + u32 len; + __isfsCtxt* blCtxt = 0; + + if (fname == NULL || (len = strnlen(fname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + memcpy(blCtxt->ioBuf, fname, len + 1); + rc = IOS_Open((const char*)blCtxt->ioBuf, access); + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +IOSFd ISFS_OpenAsync(const u8* fname, u32 access, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + u32 len; + __isfsCtxt* ctxt; + + if (fname == NULL || (len = strnlen(fname, 64)) == 64) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + memcpy(ctxt->ioBuf, fname, len + 1); + rc = IOS_OpenAsync((const char*)ctxt->ioBuf, access, _isfsFuncCb, ctxt); + +out: + return rc; +} + +static IOSError _FSGetFileStatsCb(IOSError ret, void* ctxt) { + __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt; + ISFSError rc = ISFS_ERROR_OK; + + if (ret == 0) { + memcpy(_ctxt->args.fstats, _ctxt->ioBuf, sizeof(*_ctxt->args.fstats)); + } + + return rc; +} + +s32 ISFS_GetFileStats(IOSFd fd, ISFSFileStats* stats) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt* blCtxt = 0; + + if (stats == NULL || ((u32)stats & 31)) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32); + if (blCtxt == 0) { + rc = IOS_ERROR_FAIL_ALLOC; + goto out; + } + + rc = IOS_Ioctl(fd, 11, NULL, 0, blCtxt->ioBuf, sizeof(*stats)); + + if (rc != IOS_ERROR_OK) { + goto out; + } + + memcpy(stats, blCtxt->ioBuf, sizeof(*stats)); + +out: + if (blCtxt != 0) { + if (blCtxt != 0) { + iosFree(hId, blCtxt); + } + } + + return rc; +} + +s32 ISFS_GetFileStatsAsync(IOSFd fd, ISFSFileStats* stats, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt* ctxt; + + if (stats == NULL || ((u32)stats & 31)) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 5; + ctxt->args.fstats = stats; + rc = IOS_IoctlAsync(fd, 11, NULL, 0, ctxt->ioBuf, sizeof(*stats), _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_Seek(IOSFd fd, s32 offset, u32 whence) { + ISFSError rc = ISFS_ERROR_OK; + rc = IOS_Seek(fd, offset, whence); + return rc; +} + +s32 ISFS_SeekAsync(IOSFd fd, s32 offset, u32 whence, ISFSCallback cb, void* fsCtxt) { + ISFSError rc; + __isfsCtxt* ctxt; + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + rc = IOS_SeekAsync(fd, offset, whence, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_Read(s32 fd, u8* pBuffer, u32 bufSize) { + ISFSError rc = ISFS_ERROR_OK; + + /* nullptr check and alignment to 0x20 */ + if (pBuffer == NULL || (u32)pBuffer & 31) { + rc = ISFS_ERROR_INVALID; + } else { + rc = IOS_Read(fd, pBuffer, bufSize);; + } + + return rc; +} + +s32 ISFS_ReadAsync(IOSFd fd, u8* buf, u32 size, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt* ctxt; + + if (buf == NULL || ((u32)buf & 31)) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + rc = IOS_ReadAsync(fd, buf, size, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_Write(IOSFd fd, const u8* buf, u32 size) { + ISFSError rc = ISFS_ERROR_OK; + + if (buf == NULL || ((u32)buf & 31)) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + rc = IOS_Write(fd, (u8*)buf, size); +out: + return rc; +} + +s32 ISFS_WriteAsync(IOSFd fd, const u8* buf, u32 size, ISFSCallback cb, void* fsCtxt) { + ISFSError rc = ISFS_ERROR_OK; + __isfsCtxt* ctxt; + + if (buf == NULL || ((u32)buf & (u32)(31))) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + rc = IOS_WriteAsync(fd, (void*)buf, size, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_Close(IOSFd fd) { + ISFSError rc = ISFS_ERROR_OK; + rc = IOS_Close(fd); + return rc; +} + +s32 ISFS_CloseAsync(IOSFd fd, ISFSCallback cb, void* fsCtxt) { + ISFSError rc; + __isfsCtxt* ctxt; + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + + if (ctxt == 0) { + rc = ISFS_ERROR_BUSY; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + rc = IOS_CloseAsync(fd, _isfsFuncCb, ctxt); + +out: + return rc; +} + +s32 ISFS_ShutdownAsync(ISFSCallback cb, void* fsCtxt) { + __isfsCtxt* ctxt; + s32 rc = ISFS_ERROR_OK; + + ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32); + + if (__fsFd < 0) { + rc = ISFS_ERROR_INVALID; + goto out; + } + + ctxt->cb = cb; + ctxt->ctxt = fsCtxt; + ctxt->func = 0; + rc = IOS_IoctlAsync(__fsFd, 13, NULL, 0, NULL, 0, _isfsFuncCb, ctxt); + +out: + return rc; +} diff --git a/src/revolution/ipc/ipcMain.c b/src/revolution/ipc/ipcMain.c new file mode 100644 index 0000000000..f126cb5b1f --- /dev/null +++ b/src/revolution/ipc/ipcMain.c @@ -0,0 +1,51 @@ +#include +#include +#include + +static void* IPCBufferHi; +static void* IPCBufferLo; + +static void* IPCCurrentBufferHi; +static void* IPCCurrentBufferLo; + +static u8 Initialized; + +void IPCInit(void) { + if (Initialized) { + return; + } + + IPCBufferHi = __OSGetIPCBufferHi(); + IPCBufferLo = __OSGetIPCBufferLo(); + IPCCurrentBufferHi = IPCBufferHi; + IPCCurrentBufferLo = IPCBufferLo; + + Initialized = TRUE; +} + +void IPCReInit(void) { + Initialized = FALSE; + IPCInit(); +} + +u32 IPCReadReg(u32 regIdx) { + u32 reg = __IPCRegs[regIdx]; + return reg; +} + +void IPCWriteReg(u32 regIdx, u32 data) { + __IPCRegs[regIdx] = data; +} + +void* IPCGetBufferHi(void) { + return IPCCurrentBufferHi; +} + +void* IPCGetBufferLo(void) { + return IPCCurrentBufferLo; +} + +void IPCSetBufferLo(void* newLo) { + ASSERTLINE(296, IPCBufferLo <= newLo); + IPCCurrentBufferLo = newLo; +} diff --git a/src/revolution/ipc/ipcProfile.c b/src/revolution/ipc/ipcProfile.c new file mode 100644 index 0000000000..60bf758a75 --- /dev/null +++ b/src/revolution/ipc/ipcProfile.c @@ -0,0 +1,53 @@ +#include +#include + +static u32 IpcReqPtrArray[96] = {0}; +static u32 IpcReqArray[0x300] = {0}; +static OSTime IpcStartTimeArray[96]; +static u8 IpcHandlePathBuf[0x1800]; +static u8 IpcOpenPathBuf[0x1200]; + +static u32 IpcNumPendingReqs = 0; +static u32 IpcNumUnIssuedReqs = 0; + +static void AddReqInfo(void* req); +static void DelReqInfo(void* req); + +void IPCiProfInit(void) { + u32 i; + + IpcNumPendingReqs = 0; + IpcNumUnIssuedReqs = 0; + + for (i = 0; i < 96; ++i) { + IpcReqPtrArray[i] = 0; + IpcStartTimeArray[i] = 0; + } + + memset(IpcHandlePathBuf, 0, sizeof(IpcHandlePathBuf)); + memset(IpcOpenPathBuf, 0, sizeof(IpcOpenPathBuf)); + memset(IpcReqArray, 0, sizeof(IpcReqArray)); +} + +void IPCiProfQueueReq(void* req, s32 handle) { + ++IpcNumPendingReqs; + ++IpcNumUnIssuedReqs; + AddReqInfo(req); +} + +void IPCiProfAck(void) { + --IpcNumUnIssuedReqs; +} + +void IPCiProfReply(void* req, s32 handle) { + --IpcNumPendingReqs; + DelReqInfo(req); +} + +static void AddReqInfo(void* ptr) { + // NONMATCHING +} + +static void DelReqInfo(void* ptr) { + // NONMATCHING +} diff --git a/src/revolution/ipc/ipcclt.c b/src/revolution/ipc/ipcclt.c new file mode 100644 index 0000000000..39bc8b2f69 --- /dev/null +++ b/src/revolution/ipc/ipcclt.c @@ -0,0 +1,835 @@ +#include +#include +#include +#include +#include + +/* macro for matching __ipcQueueRequest */ +#define diff(a, b) \ + ((a) < (b)) ? ((u32)0xffffffff - (b) + (a) + 1) : ((a) - (b)) + +static s32 __mailboxAck = 1; +static u32 __relnchFl = 0; + +#define OSVirtualToPhysical(addr) OSCachedToPhysical(addr) +#define OSPhysicalToVirtual(addr) OSPhysicalToCached(addr) + +typedef struct IOSRpcRequest { + IOSResourceRequest request; + IOSIpcCb cb ATTRIBUTE_ALIGN(32); // I am assuming this is aligned due to where cbArg is stored, and I see nothing between cb and callback_arg? + void* callback_arg; + u32 relaunch_flag; + OSThreadQueue thread_queue; +} IOSRpcRequest; + +static IOSRpcRequest* __relnchRpc = 0; +static IOSRpcRequest* __relnchRpcSave = 0; + +#define ROUNDUP(sz) (((u32)(sz) + 31) & ~(u32)(31)) + +static u8 __rpcBuf[ROUNDUP(sizeof(IOSRpcRequest))] ATTRIBUTE_ALIGN(32); + +static struct { + u32 rcount; + u32 wcount; + u32 rptr; + u32 wptr; + IOSResourceRequest* buf[48]; +} __responses; + +static OSAlarm __timeout_alarm; + +static IOSHeapId hid = -1; + +extern void ACRWriteReg(u32 param_0, u32 param_1); +extern void IPCiProfQueueReq(void*, s32); + +/* the MSL_C version of strnlen doesn't match when inlined, cool */ +u32 strnlen(const u8 *str, u32 n) { + const u8 *s = str; + while (*s && n-- > 0) + ++s; + return (s - str); +} + +static IOSRpcRequest* ipcAllocReq(void) { + IOSRpcRequest* req = NULL; + req = iosAllocAligned(hid, 0x40, 0x20); + return req; +} + +static IOSError ipcFree(IOSRpcRequest *rpc) { + IOSError ret = 0; + iosFree(hid, rpc); + return ret; +} + +static IOSError __ipcQueueRequest(IOSResourceRequest *req) { + IOSError ret = 0; + + if (diff(__responses.wcount, __responses.rcount) >= sizeof(__responses.buf) / sizeof(__responses.buf[0])) { + ret = -8; + } else { + __responses.buf[__responses.wptr] = req; + __responses.wptr = (__responses.wptr + 1) % (sizeof(__responses.buf) / sizeof(__responses.buf[0])); + __responses.wcount++; + IPCiProfQueueReq(req, (s32)req->handle); + } + + return ret; +} + +static void __ipcSendRequest(void) { + IOSRpcRequest *rpc; + + if (diff(__responses.wcount, __responses.rcount) <= 0) { + return; + } + + rpc = (IOSRpcRequest*)__responses.buf[__responses.rptr]; + + if (rpc == 0) { + return; + } + + if (rpc->relaunch_flag) { + __mailboxAck--; + } + + IPCWriteReg(0, OSVirtualToPhysical(rpc)); + __responses.rptr = (__responses.rptr + 1) % (sizeof(__responses.buf) / sizeof(__responses.buf[0])); + __responses.rcount++; + __mailboxAck--; + + IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 0); +} + +void IpcReplyHandler(__OSInterrupt interrupt, OSContext* context) { + OSContext exceptionContext; + IOSResourceRequest* req; + IOSRpcRequest* rep; + u32 addr; + + addr = IPCReadReg(2); + + if (!addr) { + goto err; + } + + rep = (IOSRpcRequest*)OSPhysicalToVirtual(addr); + IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4) | 1 << 2)); + ACRWriteReg(0x30, 0x40000000); + req = &rep->request; + + DCInvalidateRange(req, sizeof(*req)); + + switch (req->handle) { + case 3: + req->args.read.outPtr = (req->args.read.outPtr) ? OSPhysicalToVirtual((u32)req->args.read.outPtr) : 0; + + if (req->status > 0) { + DCInvalidateRange(req->args.read.outPtr, (u32)req->status); + } + + break; + case 6: + req->args.ioctl.outPtr = (req->args.ioctl.outPtr) ? OSPhysicalToVirtual((u32)req->args.ioctl.outPtr) : 0; + DCInvalidateRange(req->args.ioctl.inPtr, req->args.ioctl.inLen); + DCInvalidateRange(req->args.ioctl.outPtr, req->args.ioctl.outLen); + break; + case 7: + { + int i; + IOSResourceIoctlv* v = &req->args.ioctlv; + req->args.ioctlv.vector = (req->args.ioctlv.vector) ? (IOSIoVector*)OSPhysicalToVirtual((u32)req->args.ioctlv.vector) : 0; + DCInvalidateRange(&v->vector[0], (req->args.ioctlv.readCount + req->args.ioctlv.writeCount) * sizeof(IOSIoVector)); + + for (i = 0; i < (req->args.ioctlv.readCount + req->args.ioctlv.writeCount); ++i) { + v->vector[i].base = (v->vector[i].base) ? (u8*)OSPhysicalToVirtual((u32)v->vector[i].base) : 0; + DCInvalidateRange(v->vector[i].base, v->vector[i].length); + } + + if (__relnchFl && __relnchRpcSave == rep) { + __relnchFl = 0; + + if (__mailboxAck < 1) { + __mailboxAck++; + } + } + + break; + } + default: + break; + } + + if (rep->cb) { + OSClearContext(&exceptionContext); + OSSetCurrentContext(&exceptionContext); + rep->cb(req->status, (void*)rep->callback_arg); + OSClearContext(&exceptionContext); + OSSetCurrentContext(context); + ipcFree(rep); + } else { + OSWakeupThread(&rep->thread_queue); + } + + IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 3); + IPCiProfReply(req, (s32)req->handle); + +err: + return; +} + +static void IpcAckHandler(__OSInterrupt interrupt, OSContext* context) { + IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 1); + ACRWriteReg(0x30, 0x40000000); + + if (__mailboxAck < 1) { + __mailboxAck++; + IPCiProfAck(); + } + + if (__mailboxAck > 0) { + if (__relnchFl) { + IOSResourceRequest* req = &__relnchRpc->request; + req->status = 0; + __relnchFl = 0; + + OSWakeupThread(&__relnchRpc->thread_queue); + IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 3); + } + + __ipcSendRequest(); + } +} + + +void IPCInterruptHandler(__OSInterrupt interrupt, OSContext* context) { + if ((IPCReadReg(1) & (1 << 4 | 1 << 2)) == (1 << 4 | 1 << 2)) { + IpcReplyHandler(interrupt, context); + } + + if ((IPCReadReg(1) & (1 << 5 | 1 << 1)) == (1 << 5 | 1 << 1)) { + IpcAckHandler(interrupt, context); + } +} + +IOSError IPCCltInit(void) { + static u32 initialized = 0; + u32 i; + IOSError ret = 0; + void* bufferLo; + + if (initialized) { + goto out; + } + + initialized = 1; + + IPCInit(); + + i = ROUNDUP(64 * (ROUNDUP(sizeof(IOSRpcRequest)) + 64)); + bufferLo = IPCGetBufferLo(); + + if ((void*)((u8*)bufferLo + i) > IPCGetBufferHi()) { + ret = -22; + goto out; + } + + hid = iosCreateHeap(bufferLo, i); + IPCSetBufferLo((void*)((u8*)bufferLo + i)); + + __OSSetInterruptHandler(27, IPCInterruptHandler); + __OSUnmaskInterrupts(16); + + IPCWriteReg(1, (1 << 5 | 1<< 4 | 1 << 3)); + IPCiProfInit(); + OSCreateAlarm(&__timeout_alarm); + +out: + return ret; +} + +IOSError IPCCltReInit(void) { + u32 i; + IOSError ret = 0; + void* bufferLo; + + i = ROUNDUP(64 * ROUNDUP(sizeof(IOSRpcRequest))); + bufferLo = IPCGetBufferLo(); + + if ((void*)((u8*)bufferLo + i) > IPCGetBufferHi()) { + ret = -22; + goto out; + } + + hid = iosCreateHeap(bufferLo, i); + IPCSetBufferLo((void*)((u8*)bufferLo + i)); + +out: + return ret; +} + +static IOSError __ios_Ipc1(IOSFd fd, u32 cmd, IOSIpcCb cb, void* cbArg, IOSRpcRequest** rpc) { + IOSError ret = 0; + IOSResourceRequest *req; + + if (rpc == 0) { + ret = -4; + goto error; + } + + *rpc = (IOSRpcRequest*)ipcAllocReq(); + + if (*rpc == 0) { + ret = -22; + goto error; + } + + req = &(*rpc)->request; + (*rpc)->cb = cb; + (*rpc)->callback_arg = cbArg; + (*rpc)->relaunch_flag = 0; + req->cmd = cmd; + req->handle = (u32)fd; + +error: + return ret; +} + +static IOSError __ios_Ipc2(IOSRpcRequest* rpc, IOSIpcCb cb) { + IOSError ret = 0; + u32 inten; + IOSResourceRequest* req; + + if (rpc == 0) { + ret = -4; + } else { + req = &rpc->request; + + if (!cb) { + OSInitThreadQueue(&rpc->thread_queue); + } + + DCFlushRange(req, sizeof(*req)); + inten = OSDisableInterrupts(); + ret = __ipcQueueRequest(req); + + if (ret != 0) { + OSRestoreInterrupts(inten); + + if (cb) { + ipcFree(rpc); + } + } else { + if (__mailboxAck > 0) { + __ipcSendRequest(); + } + + if (!cb) { + OSSleepThread(&rpc->thread_queue); + } + + OSRestoreInterrupts(inten); + + if (!cb) { + ret = req->status; + } + } + } + + if (rpc && !cb) { + ipcFree(rpc); + } + + return ret; +} + +static IOSError __ios_Open(IOSRpcRequest* rpc, const char* path, u32 flags) { + IOSError ret = 0; + IOSResourceRequest* req; + + if (!rpc) { + ret = -4; + goto error; + } + + req = &rpc->request; + DCFlushRange((void*)path, strnlen((const u8*)path, 64) + 1); + req->args.open.path = (u8*)OSVirtualToPhysical((void*)path); + req->args.open.flags = flags; + +error: + return ret; +} + +IOSError IOS_OpenAsync(const char* pPath, u32 flags, IOSIpcCb cb, void* callback_arg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(0, 1, cb, callback_arg, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Open(rpc, pPath, flags); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, cb); +error: + return ret; +} + +IOSError IOS_Open(const char* path, u32 flags) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(0, 1, 0, 0, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Open(rpc, path, flags); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, 0); + +error: + return ret; +} + +IOSError IOS_CloseAsync(IOSFd fd, IOSIpcCb cb, void* cbArg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 2, cb, cbArg, &rpc); + + if (ret == 0) { + ret = __ios_Ipc2(rpc, cb); + } + + return ret; +} + +IOSError IOS_Close(IOSFd fd) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 2, 0, 0, &rpc); + + if (ret == 0) { + ret = __ios_Ipc2(rpc, 0); + } + + return ret; +} + +static IOSError __ios_Read(IOSRpcRequest* rpc, void* buf, u32 len) { + IOSError ret = 0; + IOSResourceRequest* req; + + if (!rpc) { + ret = -4; + goto error; + } + + req = &rpc->request; + + DCInvalidateRange(buf, len); + req->args.read.outPtr = (buf) ? (u8*)OSVirtualToPhysical(buf) : 0; + req->args.read.outLen = len; + +error: + return ret; +} + +IOSError IOS_ReadAsync(IOSFd fd, void* buf, u32 len, IOSIpcCb cb, void* cbArg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 3, cb, cbArg, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Read(rpc, buf, len); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, cb); + +error: + return ret; +} + +IOSError IOS_Read(IOSFd fd, void* buf, u32 len) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 3, 0, 0, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Read(rpc, buf, len); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, 0); + +error: + return ret; +} + +static IOSError __ios_Write(IOSRpcRequest* rpc, void* buf, u32 len) { + IOSError ret = 0; + IOSResourceRequest* req; + + if (!rpc) { + ret = -4; + goto error; + } + + req = &rpc->request; + req->args.write.inPtr = (buf) ? (u8*)OSVirtualToPhysical(buf) : 0; + req->args.write.inLen = len; + DCFlushRange(buf, len); + +error: + return ret; +} + +IOSError IOS_WriteAsync(IOSFd fd, void* buf, u32 len, IOSIpcCb cb, void* cbArg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 4, cb, cbArg, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Write(rpc, buf, len); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, cb); + +error: + return ret; +} + +IOSError IOS_Write(IOSFd fd, void* buf, u32 len) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 4, 0, 0, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Write(rpc, buf, len); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, 0); + +error: + return ret; +} + +static IOSError __ios_Seek(IOSRpcRequest* rpc, s32 offset, u32 whence) { + IOSError ret = 0; + IOSResourceRequest* req; + + if (!rpc) { + ret = -4; + goto error; + } + + req = &rpc->request; + req->args.seek.offset = offset; + req->args.seek.whence = whence; + +error: + return ret; +} + +IOSError IOS_SeekAsync(IOSFd fd, s32 offset, u32 whence, IOSIpcCb cb, void* cbArg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 5, cb, cbArg, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Seek(rpc, offset, whence); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, cb); + +error: + return ret; +} + +IOSError IOS_Seek(IOSFd fd, s32 offset, u32 whence) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 5, 0, 0, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Seek(rpc, offset, whence); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, 0); + +error: + return ret; +} + +static IOSError __ios_Ioctl(IOSRpcRequest* rpc, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen) { + IOSError ret = 0; + IOSResourceRequest* req; + + if (!rpc) { + ret = -4; + goto error; + } + + req = &rpc->request; + + req->args.ioctl.cmd = (u32)cmd; + req->args.ioctl.outPtr = (output) ? (u8*)OSVirtualToPhysical(output) : 0; + req->args.ioctl.outLen = outputLen; + req->args.ioctl.inPtr = (input) ? (u8*)OSVirtualToPhysical(input) : 0; + req->args.ioctl.inLen = inputLen; + + DCFlushRange(input, inputLen); + DCFlushRange(output, outputLen); + +error: + return ret; +} + +IOSError IOS_IoctlAsync(IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen, IOSIpcCb cb, void* cbArg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 6, cb, cbArg, &rpc); + + if (ret != 0) { + goto err; + } + + ret = __ios_Ioctl(rpc, cmd, input, inputLen, output, outputLen); + + if (ret != 0) { + goto err; + } + + ret = __ios_Ipc2(rpc, cb); + +err: + return ret; +} + +IOSError IOS_Ioctl(IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 6, 0, 0, &rpc); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ioctl(rpc, cmd, input, inputLen, output, outputLen); + + if (ret != 0) { + goto error; + } + + ret = __ios_Ipc2(rpc, 0); + +error: + return ret; +} + +static IOSError __ios_Ioctlv(IOSRpcRequest* rpc, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect) { + IOSError ret = 0; + IOSResourceRequest* req; + IOSResourceIoctlv* v; + u32 i, j; + + if (!rpc) { + ret = -4; + goto err; + } + + req = &rpc->request; + req->args.ioctlv.cmd = (u32)cmd; + req->args.ioctlv.readCount = readCount; + req->args.ioctlv.writeCount = writeCount; + req->args.ioctlv.vector = vect; + + v = &req->args.ioctlv; + + for (i = 0, j = v->readCount; i < req->args.ioctlv.writeCount; ++i) { + DCFlushRange(v->vector[j + i].base, v->vector[j + i].length); + v->vector[j + i].base = (v->vector[j + i].base) ? (u8*)OSVirtualToPhysical(v->vector[j + i].base) : 0; + } + + for (i = 0; i < req->args.ioctlv.readCount; ++i) { + DCFlushRange(v->vector[i].base, v->vector[i].length); + v->vector[i].base = (v->vector[i].base) ? (u8*)OSVirtualToPhysical(v->vector[i].base) : 0; + } + + DCFlushRange(&v->vector[0], (v->readCount + v->writeCount) * sizeof(IOSIoVector)); + req->args.ioctlv.vector = (vect) ? (IOSIoVector*)OSVirtualToPhysical(vect) : 0; + +err: + return ret; +} + +IOSError IOS_IoctlvAsync(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect, IOSIpcCb cb, void* cbArg) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 7, cb, cbArg, &rpc); + + if (ret != 0) { + goto err; + } + + ret = __ios_Ioctlv(rpc, cmd, readCount, writeCount, vect); + + if (ret != 0) { + goto err; + } + + ret = __ios_Ipc2(rpc, cb); + +err: + return ret; +} + +IOSError IOS_Ioctlv(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect) { + IOSRpcRequest* rpc; + IOSError ret = 0; + + ret = __ios_Ipc1(fd, 7, 0, 0, &rpc); + + if (ret != 0) { + goto err; + } + + ret = __ios_Ioctlv(rpc, cmd, readCount, writeCount, vect); + + if (ret != 0) { + goto err; + } + + ret = __ios_Ipc2(rpc, 0); + +err: + return ret; +} + +IOSError IOS_IoctlvReboot(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect) { + IOSRpcRequest* rpc; + IOSError ret = 0; + u32 inten; + IOSResourceRequest* req; + + inten = OSDisableInterrupts(); + + if (__relnchFl) { + OSRestoreInterrupts(inten); + ret = -10; + goto finish; + } + + __relnchFl = 1; + OSRestoreInterrupts(inten); + + ret = __ios_Ipc1(fd, 7, 0, 0, &rpc); + + if (ret != 0) { + goto err; + } + + __relnchRpcSave = rpc; + rpc->relaunch_flag = 1; + + ret = __ios_Ioctlv(rpc, cmd, readCount, writeCount, vect); + + if (ret != 0) { + goto err; + } + + memcpy(&__rpcBuf, rpc, sizeof(IOSRpcRequest)); + __relnchRpc = (IOSRpcRequest*)&__rpcBuf; + req = &rpc->request; + + OSInitThreadQueue(&__relnchRpc->thread_queue); + DCFlushRange(req, sizeof(*req)); + + inten = OSDisableInterrupts(); + ret = __ipcQueueRequest(req); + + if (ret != 0) { + OSRestoreInterrupts(inten); + goto err; + } + + if (__mailboxAck > 0) { + __ipcSendRequest(); + } + + OSSleepThread(&__relnchRpc->thread_queue); + OSRestoreInterrupts(inten); + ret = (&__relnchRpc->request)->status; + +err: + __relnchFl = 0; + __relnchRpcSave = NULL; + + if (rpc && (ret != 0)) { + ipcFree(rpc); + } + +finish: + return ret; +} diff --git a/src/revolution/ipc/memory.c b/src/revolution/ipc/memory.c new file mode 100644 index 0000000000..29625a117e --- /dev/null +++ b/src/revolution/ipc/memory.c @@ -0,0 +1,240 @@ +#include +#include + +typedef struct IOSChunk { + u32 status; + u32 size; + struct IOSChunk* prevFree; + struct IOSChunk* nextFree; +} IOSChunk; + +typedef struct { + void* base; + u32 owner; + u32 size; + IOSChunk *freeList; +} IOSHeap; + +static IOSHeap __heaps[8]; + +static void __iosCoalesceChunk(IOSChunk* p) { + if (p && ((u32)p->nextFree == ((u32)p + p->size + sizeof(IOSChunk)))) { + IOSChunk* next = p->nextFree; + p->nextFree = next->nextFree; + + if (p->nextFree != 0) { + p->nextFree->prevFree = p; + } + + p->size += next->size + sizeof(IOSChunk); + } +} + +IOSHeapId iosCreateHeap(void* ptr, u32 size) { + IOSError rv = -4; + u32 mask; + s32 i; + IOSHeap* h; + + mask = OSDisableInterrupts(); + + if ((u32)ptr & 31) { + goto finish; + } + + for (i = 0; i < sizeof(__heaps) / sizeof(__heaps[0]); ++i) { + if (__heaps[i].base == 0) { + break; + } + } + + if (i == 8) { + rv = -5; + goto finish; + } + + h = __heaps + i; + h->base = ptr; + h->size = size; + h->freeList = ptr; + h->freeList->status = 0xBABE0000; + h->freeList->size = size - sizeof(IOSChunk); + h->freeList->prevFree = NULL; + h->freeList->nextFree = NULL; + rv = i; + +finish: + OSRestoreInterrupts(mask); + return rv; +} + +#define CHECK_HEAP_ID(id, err) \ + if (((id) < 0) || ((id) >= 8) || (!__heaps[id].base)) { \ + rv = err; \ + goto finish; \ + } + +#define GET_HEAP(h, id, err) \ + do { \ + CHECK_HEAP_ID(id, err); \ + h = __heaps + id; \ + } while (0) + +void* __iosAlloc(IOSHeapId id, u32 size, u32 alignment) { + u32 mask; + IOSChunk* p, *best; + IOSHeap* h; + void* rv = NULL; + + mask = OSDisableInterrupts(); + + if (!size) { + goto finish; + } + + if (!alignment || (alignment & (alignment - 1))) { + goto finish; + } + + if (alignment < 32) { + alignment = 32; + } + + size = (size + 31) & ~31; + + GET_HEAP(h, id, NULL); + + p = h->freeList; + best = NULL; + + while (p != 0) { + u32 ptr = (u32)p + sizeof(IOSChunk); + u32 extra = (alignment - (ptr & (alignment - 1))) & (alignment - 1); + + if ((p->size == size) && !extra) { + best = p; + break; + } else if ((p->size >= (size + extra)) && (!best || (p->size < best->size))) { + best = p; + } + + p = p->nextFree; + } + + p = best; + + if (p != 0) { + u32 ptr = (u32)p + sizeof(IOSChunk); + u32 extra = (alignment - (ptr & (alignment - 1))) & (alignment - 1); + + if (p->size > (size + extra + sizeof(IOSChunk))) { + IOSChunk *n = (IOSChunk *)((u8 *)p + size + extra + sizeof(IOSChunk)); + n->status = 0xBABE0000; + n->size = p->size - size - extra - sizeof(IOSChunk); + n->nextFree = p->nextFree; + + if (n->nextFree) { + n->nextFree->prevFree = n; + } + + p->nextFree = n; + p->size = size + extra; + } + + p->status = 0xBABE0001; + + if (p->prevFree) { + p->prevFree->nextFree = p->nextFree; + } else { + h->freeList = p->nextFree; + } + + if (p->nextFree) { + p->nextFree->prevFree = p->prevFree; + } + + p->prevFree = p->nextFree = NULL; + rv = (u8*)p + extra + sizeof(IOSChunk); + + if (extra) { + IOSChunk* n = (IOSChunk*)rv - 1; + n->status = 0xBABE0002; + n->prevFree = p; + } + } + +finish: + OSRestoreInterrupts(mask); + return rv; +} + +void* iosAllocAligned(IOSHeapId id, u32 size, u32 alignment) { + return __iosAlloc(id, size, alignment); +} + +IOSError iosFree(IOSHeapId id, void* ptr) { + u32 mask; + IOSChunk* p; + IOSHeap* h; + IOSError rv = -4; + IOSChunk* prev; + + mask = OSDisableInterrupts(); + + if (!ptr) { + goto finish; + } + + GET_HEAP(h, id, -4); + + if (((u32)ptr) < (((u32)h->base) + sizeof(IOSChunk)) || ((u32)ptr) > ((u32)h->base + h->size)) { + goto finish; + } + + p = (IOSChunk*)ptr - 1; + + if (p->status == 0xBABE0002) { + p = p->prevFree; + } + + if (p->status != 0xBABE0001) { + goto finish; + } + + p->status = 0xBABE0000; + prev = h->freeList; + + while (prev != 0) { + if (!prev->nextFree || prev->nextFree > p) { + break; + } + + prev = prev->nextFree; + } + + if (prev && p > prev) { + p->prevFree = prev; + p->nextFree = prev->nextFree; + prev->nextFree = p; + + if (p->nextFree) { + p->nextFree->prevFree = p; + } + } else { + p->nextFree = h->freeList; + h->freeList = p; + p->prevFree = NULL; + + if (p->nextFree) { + p->nextFree->prevFree = p; + } + } + + __iosCoalesceChunk(p); + __iosCoalesceChunk(p->prevFree); + rv = 0; + +finish: + OSRestoreInterrupts(mask); + return rv; +} diff --git a/src/revolution/nand/NANDCheck.c b/src/revolution/nand/NANDCheck.c new file mode 100644 index 0000000000..3f7630af12 --- /dev/null +++ b/src/revolution/nand/NANDCheck.c @@ -0,0 +1,94 @@ +#include +#include + +static const char* USER_DIR_LIST[] = { + "/meta", + "/ticket", + "/title/00010000", + "/title/00010001", + "/title/00010003", + "/title/00010004", + "/title/00010005", + "/title/00010006", + "/title/00010007", + "/shared2/title", + NULL, +}; + +s32 nandCalcUsage(u32* fsBlock, u32* inode, const char* dir_list[]) { + ISFSError ret = ISFS_ERROR_UNKNOWN; + *fsBlock = 0; + *inode = 0; + + while (*dir_list) { + u32 blk = 0; + u32 node = 0; + ret = ISFS_GetUsage((const u8*)*dir_list, &blk, &node); + + if (ret == ISFS_ERROR_OK) { + *fsBlock += blk; + *inode += node; + } else if (ret == ISFS_ERROR_NOEXISTS) { + ret = ISFS_ERROR_OK; + } else { + break; + } + + ++dir_list; + } + + return ret; +} + +ISFSError nandCalcUserUsage(u32* fsBlock, u32* inode) { + return nandCalcUsage(fsBlock, inode, USER_DIR_LIST); +} + +static u32 nandCheck(const u32 reqBlock, const u32 reqInode, const u32 homeBlock, const u32 homeInode, const u32 userBlock, const u32 userInode) { + u32 answer = 0; + + if (homeBlock + reqBlock > 0x400) { + answer |= 1; + } + + if (homeInode + reqInode > 0x21) { + answer |= 2; + } + + if (userBlock + reqBlock > 0x4400) { + answer |= 4; + } + + if (userInode + reqInode > 0xFA0) { + answer |= 8; + } + + return answer; +} + +s32 NANDCheck(const u32 fsBlock, const u32 inode, u32* answer) { + ISFSError ret = ISFS_ERROR_UNKNOWN; + u32 homeBlocks = 0xFFFFFFFF; + u32 homeInodes = 0xFFFFFFFF; + u32 userBlocks = 0xFFFFFFFF; + u32 userInodes = 0xFFFFFFFF; + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + ret = ISFS_GetUsage((const u8*)nandGetHomeDir(), &homeBlocks, &homeInodes); + + if (ret != ISFS_ERROR_OK) { + return nandConvertErrorCode(ret); + } + + ret = nandCalcUserUsage(&userBlocks, &userInodes); + + if (ret != ISFS_ERROR_OK) { + return nandConvertErrorCode(ret); + } + + *answer = nandCheck(fsBlock, inode, homeBlocks, homeInodes, userBlocks, userInodes); + return NAND_RESULT_OK; +} diff --git a/src/revolution/nand/NANDCore.c b/src/revolution/nand/NANDCore.c new file mode 100644 index 0000000000..575ebecb40 --- /dev/null +++ b/src/revolution/nand/NANDCore.c @@ -0,0 +1,514 @@ +#include +#include +#include +#include +#include + +#ifdef SDK_AUG2010 +#define BUILD_DATE "Aug 23 2010" +#if DEBUG +#define BUILD_TIME "17:28:24" +#else +#define BUILD_TIME "17:33:06" +#endif +#elif SDK_SEP2006 +#define BUILD_DATE "Sep 21 2006" +#define BUILD_TIME "14:32:13" +#endif + +#ifdef SDK_AUG2010 +#if DEBUG +const char* __NANDVersion = "<< RVL_SDK - NAND \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>"; +#else +const char* __NANDVersion = "<< RVL_SDK - NAND \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>"; +#endif +#elif SDK_SEP2006 +const char* __NANDVersion = "<< RVL_SDK - NAND \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>"; +#endif + +enum LibState { + STATE_NOT_INITIALIZED, + STATE_WORKING, + STATE_INITIALIZED +}; + +static enum LibState s_libState = STATE_NOT_INITIALIZED; +static char s_currentDir[64] ATTRIBUTE_ALIGN(32) = "/"; +static char s_homeDir[64] ATTRIBUTE_ALIGN(32) = ""; + +static BOOL nandOnShutdown(BOOL final, u32 event); +void nandConvertPath(char* abspath, const char* wd, const char* relpath); +static void nandShutdownCallback(ISFSError result, void* ctxt); + +static OSShutdownFunctionInfo s_shutdownFuncInfo = { + nandOnShutdown, + 255 +}; + +void nandRemoveTailToken(char* newpath, const char* oldpath) { + ASSERTMSGLINE(189, newpath, "null pointer is detected in argument of newpath\n"); + ASSERTMSGLINE(190, oldpath, "null pointer is detected in argument of oldpath\n"); + ASSERTMSGLINE(191, oldpath[0] == '/', "Head of path must be \'/\' .\n"); + + if (oldpath[0] == '/' && oldpath[1] == '\0') { + newpath[0] = '/'; + newpath[1] = '\0'; + } else { + int i = 0; + + for (i = (int)(strlen(oldpath)) - 1; i >= 0; --i) { + if (oldpath[i] == '/') { + if (i != 0) { + strncpy(newpath, oldpath, (u32)i); + newpath[i] = '\0'; + break; + } else { + newpath[0] = '/'; + newpath[1] = '\0'; + break; + } + } + } + } +} + +void nandGetHeadToken(char* token, char* newpath, const char* oldpath) { + unsigned int i = 0; + + ASSERTMSGLINE(224, token && newpath && oldpath, "Null pointer detected!\n"); + ASSERTMSGLINE(225, strlen(oldpath) != 0, "Null string was detected!\n"); + ASSERTMSGLINE(226, oldpath[0] != '/', "Head of relative path must not be \'/\' .\n"); + + for (i = 0; i <= strlen(oldpath); ++i) { + if (oldpath[i] == '/') { + strncpy(token, oldpath, i); + token[i] = '\0'; + + if (oldpath[i + 1] == '\0') { + newpath[0] = '\0'; + } else { + strcpy(newpath, oldpath + i + 1); + } + + break; + } else if (oldpath[i] == '\0') { + strncpy(token, oldpath, i); + token[i] = '\0'; + newpath[0] = '\0'; + break; + } + } +} + +void nandGetRelativeName(char* name, const char* path) { + if (strcmp("/", path) == 0) { + strcpy(name, ""); + } else { + int i = 0; + + ASSERTMSGLINE(260, nandIsAbsolutePath(path), "path must be absolute path."); + ASSERTMSGLINE(261, path[strlen(path) - 1] != '/', "path must not be terminated with \'/\'."); + + for (i = (int)(strlen(path) - 1); i >= 0; --i) { + if (path[i] == '/') { + break; + } + } + + ASSERTMSGLINE(270, strlen(path + i + 1) <= ISFS_INODE_NAMELEN, "path must be smaller than or equal to ISFS_INODE_NAMELEN."); + + strcpy(name, path + i + 1); + } +} + +void nandConvertPath(char* abspath, const char* wd, const char* relpath) { + char token[128]; + char new_relpath[128]; + + ASSERTMSGLINE(283, abspath && wd && relpath, "Null pointer detected!\n"); + ASSERTMSGLINE(284, wd[0] == '/', "Head of \'wd\' must be \'/\'.\n"); + + if (strlen(relpath) == 0) { + strcpy(abspath, wd); + return; + } + + nandGetHeadToken(token, new_relpath, relpath); + + if (strcmp(token, ".") == 0) { + nandConvertPath(abspath, wd, new_relpath); + } else if (strcmp(token, "..") == 0) { + char new_wd[128]; + nandRemoveTailToken(new_wd, wd); + nandConvertPath(abspath, new_wd, new_relpath); + } else if (token[0] != '\0') { + char new_wd[128]; + if (strcmp(wd, "/") == 0) { + sprintf(new_wd, "/%s", token); + } else { + sprintf(new_wd, "%s/%s", wd, token); + } + + nandConvertPath(abspath, new_wd, new_relpath); + } else { + strcpy(abspath, wd); + } +} + +BOOL nandIsRelativePath(const char* path) { + if (path[0] == '/') { + return FALSE; + } else { + return TRUE; + } +} + +BOOL nandIsAbsolutePath(const char* path) { + if (nandIsRelativePath(path)) { + return FALSE; + } else { + return TRUE; + } +} + +BOOL nandIsPrivatePath(const char* path) { + if (strncmp(path, "/shared2", 8) == 0) { + return TRUE; + } else { + return FALSE; + } +} + +BOOL nandIsUnderPrivatePath(const char* path) { + if (strncmp(path, "/shared2/", 9) == 0 && path[9] != '\0') { + return TRUE; + } else { + return FALSE; + } +} + +BOOL nandIsInitialized(void) { + if (s_libState == STATE_INITIALIZED) { + return TRUE; + } else { + return FALSE; + } +} + +void nandReportErrorCode(const ISFSError err) { + // NONMATCHING + return; +} + +void nandLoggingCallback(BOOL, ISFSError err) { + if (err == ISFS_ERROR_UNKNOWN || err == IOS_ERROR_UNKNOWN) { + __NANDPrintErrorMessage(err); + } +} + +s32 nandConvertErrorCode(const ISFSError err) { + const int ERRMAP[] = { + ISFS_ERROR_OK, NAND_RESULT_OK, + ISFS_ERROR_ACCESS, NAND_RESULT_ACCESS, + ISFS_ERROR_CORRUPT, NAND_RESULT_CORRUPT, + ISFS_ERROR_ECC_CRIT, NAND_RESULT_ECC_CRIT, + ISFS_ERROR_EXISTS, NAND_RESULT_EXISTS, + ISFS_ERROR_HMAC, NAND_RESULT_AUTHENTICATION, + ISFS_ERROR_INVALID, NAND_RESULT_INVALID, + ISFS_ERROR_MAXBLOCKS, NAND_RESULT_MAXBLOCKS, + ISFS_ERROR_MAXFD, NAND_RESULT_MAXFD, + ISFS_ERROR_MAXFILES, NAND_RESULT_MAXFILES, + ISFS_ERROR_MAXDEPTH, NAND_RESULT_MAXDEPTH, + ISFS_ERROR_NOEXISTS, NAND_RESULT_NOEXISTS, + ISFS_ERROR_NOTEMPTY, NAND_RESULT_NOTEMPTY, + ISFS_ERROR_NOTREADY, NAND_RESULT_UNKNOWN, + ISFS_ERROR_OPENFD, NAND_RESULT_OPENFD, + ISFS_ERROR_UNKNOWN, NAND_RESULT_UNKNOWN, + ISFS_ERROR_BUSY, NAND_RESULT_BUSY, + ISFS_ERROR_SHUTDOWN, NAND_RESULT_FATAL_ERROR, + + IOS_ERROR_ACCESS, NAND_RESULT_ACCESS, + IOS_ERROR_EXISTS, NAND_RESULT_EXISTS, + IOS_ERROR_INTR, NAND_RESULT_UNKNOWN, + IOS_ERROR_INVALID, NAND_RESULT_INVALID, + IOS_ERROR_MAX, NAND_RESULT_UNKNOWN, + IOS_ERROR_NOEXISTS, NAND_RESULT_NOEXISTS, + IOS_ERROR_QEMPTY, NAND_RESULT_UNKNOWN, + IOS_ERROR_QFULL, NAND_RESULT_BUSY, + IOS_ERROR_UNKNOWN, NAND_RESULT_UNKNOWN, + IOS_ERROR_NOTREADY, NAND_RESULT_UNKNOWN, + IOS_ERROR_ECC, NAND_RESULT_UNKNOWN, + IOS_ERROR_ECC_CRIT, NAND_RESULT_ECC_CRIT, + IOS_ERROR_BADBLOCK, NAND_RESULT_UNKNOWN, + + IOS_ERROR_INVALID_OBJTYPE, NAND_RESULT_UNKNOWN, + IOS_ERROR_INVALID_RNG, NAND_RESULT_UNKNOWN, + IOS_ERROR_INVALID_FLAG, NAND_RESULT_UNKNOWN, + IOS_ERROR_INVALID_FORMAT, NAND_RESULT_UNKNOWN, + IOS_ERROR_INVALID_VERSION, NAND_RESULT_UNKNOWN, + IOS_ERROR_INVALID_SIGNER, NAND_RESULT_UNKNOWN, + IOS_ERROR_FAIL_CHECKVALUE, NAND_RESULT_UNKNOWN, + IOS_ERROR_FAIL_INTERNAL, NAND_RESULT_UNKNOWN, + IOS_ERROR_FAIL_ALLOC, NAND_RESULT_ALLOC_FAILED, + IOS_ERROR_INVALID_SIZE, NAND_RESULT_UNKNOWN, + }; + + int i = 0; + + if (err >= 0) { + return err; + } + + for (; i < sizeof(ERRMAP) / 4; i = i + 2) { + if (ERRMAP[i] == err) { + if (err == ISFS_ERROR_ECC_CRIT || err == ISFS_ERROR_HMAC || err == ISFS_ERROR_UNKNOWN || err == IOS_ERROR_UNKNOWN || err == IOS_ERROR_ECC_CRIT) { + char buf[128] ATTRIBUTE_ALIGN(64); + sprintf(buf, "ISFS error code: %d", err); + NANDLoggingAddMessageAsync(nandLoggingCallback, err, buf); + } + + nandReportErrorCode(err); + + if (err == ISFS_ERROR_MAXBLOCKS || err == ISFS_ERROR_MAXFILES || err == ISFS_ERROR_CORRUPT || err == ISFS_ERROR_BUSY || err == IOS_ERROR_QFULL || err == IOS_ERROR_FAIL_ALLOC) { + __NANDPrintErrorMessage(err); + } + + return ERRMAP[i + 1]; + } + } + + OSReport("CAUTION! Unexpected error code [%d] was found.\n", err); + { + char buf[128] ATTRIBUTE_ALIGN(64); + sprintf(buf, "ISFS unexpected error code: %d", err); + NANDLoggingAddMessageAsync(nandLoggingCallback, err, buf); + } + + nandReportErrorCode(err); + return -64; +} + +void nandGenerateAbsPath(char* absPath, const char* path) { + if (strlen(path) == 0) { + strcpy(absPath, ""); + } else if (nandIsRelativePath(path)) { + nandConvertPath(absPath, s_currentDir, path); + } else { + u32 len = 0xFFFFFFFF; + strcpy(absPath, path); + + len = strlen(absPath); + if (len > 0) { + if ((absPath[len - 1] == '/') && (len - 1 != 0)) { + absPath[len - 1] = '\0'; + } + } + } +} + +void nandGetParentDirectory(char* parentDir, const char* absPath) { + int i = 0; + for (i = (int)strlen(absPath); i >= 0; --i) { + if (absPath[i] == '/') { + break; + } + } + + if (i == 0) { + strcpy(parentDir, "/"); + } else { + strncpy(parentDir, absPath, (u32)i); + parentDir[i] = '\0'; + } +} + +s32 NANDInit(void) { + BOOL enabled = OSDisableInterrupts(); + + if (s_libState == STATE_WORKING) { + OSRestoreInterrupts(enabled); + return -3; + } else if (s_libState == 2) { + OSRestoreInterrupts(enabled); + return 0; + } else { + ISFSError result = ISFS_ERROR_UNKNOWN; + s_libState = STATE_WORKING; + OSRestoreInterrupts(enabled); + + result = ISFS_OpenLib(); + if (result == 0) { + s32 rv; + ESTitleId id; + + rv = ESP_InitLib(); + + if (rv == 0) { + rv = ESP_GetTitleId(&id); + } + + if (rv == 0) { + rv = ESP_GetDataDir(id, s_homeDir); + } + + if (rv == 0) { + strcpy(s_currentDir, s_homeDir); + } + + ESP_CloseLib(); + + if (rv != 0) { + OSReport("Failed to set home directory.\n"); + } + + OSRegisterShutdownFunction(&s_shutdownFuncInfo); + enabled = OSDisableInterrupts(); + s_libState = STATE_INITIALIZED; + OSRestoreInterrupts(enabled); + NANDSetAutoErrorMessaging(1); + OSRegisterVersion(__NANDVersion); + return 0; + } else { + enabled = OSDisableInterrupts(); + s_libState = STATE_NOT_INITIALIZED; + OSRestoreInterrupts(enabled); + return nandConvertErrorCode(result); + } + } +} + +static BOOL nandOnShutdown(BOOL final, u32 event) { + if (!final) { + if (event == 2) { + volatile BOOL flag = FALSE; + OSTime t = OSGetTime(); + ISFS_ShutdownAsync(nandShutdownCallback, (void*)&flag); + + while (OSTicksToMilliseconds(OSGetTime() - t) < 500) { + if (flag) { + break; + } + } + } + + return TRUE; + } else { + return TRUE; + } +} + +static void nandShutdownCallback(ISFSError result, void* ctxt) { + (void)result; + *(BOOL*)ctxt = TRUE; +} + +// NOTE: function doesn't exist in TP debug, needed for string data +s32 NANDGetCurrentDir() { + OSReport("NANDGetCurrentDir"); +} + +s32 NANDGetHomeDir(char* path) { + ASSERTMSG1LINE(0, path, "NULL pointer is detected: %s()", "NANDGetHomeDir"); + + if (!nandIsInitialized()) { + return -128; + } + + strcpy(path, s_homeDir); + return 0; +} + +void nandCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); +} + +void nandGetTypeCallback(ISFSError result, void* ctxt); + +static ISFSError nandGetType(const char* path, u8* type, NANDCommandBlock* block, const BOOL async_flag, const BOOL privilege_flag) { + ASSERTMSG1LINE(1133, path, "NULL pointer is detected: %s()", __FUNCTION__); + ASSERTMSG1LINE(1134, type, "NULL pointer is detected: %s()", __FUNCTION__); + + if (strlen(path) == 0) { + return ISFS_ERROR_INVALID; + } + + if (async_flag) { + nandGenerateAbsPath(block->absPath, path); + if (!privilege_flag && nandIsUnderPrivatePath(block->absPath)) { + return ISFS_ERROR_ACCESS; + } else { + block->type = type; + return ISFS_ReadDirAsync((u8*)(block->absPath), NULL, &(block->num), nandGetTypeCallback, block); + } + } else { + char absPath[64] = ""; + nandGenerateAbsPath(absPath, path); + + if (!privilege_flag && nandIsUnderPrivatePath(absPath)) { + return ISFS_ERROR_ACCESS; + } else { + u32 dmy = 0; + ISFSError err = ISFS_ReadDir((u8*)absPath, NULL, &dmy); + + if (err == ISFS_ERROR_OK || err == ISFS_ERROR_ACCESS) { + *type = 2; + err = ISFS_ERROR_OK; + } else if (err == ISFS_ERROR_INVALID) { + *type = 1; + err = ISFS_ERROR_OK; + } + + return err; + } + } +} + +s32 NANDPrivateGetTypeAsync(const char* path, u8* type, NANDCallback cb, NANDCommandBlock* block) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(nandGetType(path, type, block, TRUE, TRUE)); +} + +void nandGetTypeCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result == ISFS_ERROR_OK || result == ISFS_ERROR_ACCESS) { + *(b->type) = 2; + result = ISFS_ERROR_OK; + } else if (result == ISFS_ERROR_INVALID) { + *(b->type) = 1; + result = ISFS_ERROR_OK; + } + + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); +} + +const char* nandGetHomeDir(void) { + return s_homeDir; +} + +void NANDInitBanner(NANDBanner* bnr, u32 const flag, const u16* title, const u16* comment) { + ASSERTMSG1LINE(1253, bnr, "Null pointer is detected at %s()", __FUNCTION__); + ASSERTMSG1LINE(1254, title, "Null pointer is detected at %s()", __FUNCTION__); + ASSERTMSG1LINE(1255, comment, "Null pointer is detected at %s()", __FUNCTION__); + + memset(bnr, 0, sizeof(NANDBanner)); + bnr->signature = 0x5749424E; + bnr->flag = flag; + + if (wcscmp((wchar_t*)title, L"") == 0) { + wcsncpy(bnr->comment[0], L" ", 32); + } else { + wcsncpy(bnr->comment[0], (wchar_t*)title, 32); + } + + if (wcscmp((wchar_t*)comment, L"") == 0) { + wcsncpy(bnr->comment[1], L" ", 32); + } else { + wcsncpy(bnr->comment[1], (wchar_t*)comment, 32); + } +} diff --git a/src/revolution/nand/NANDErrorMessage.c b/src/revolution/nand/NANDErrorMessage.c new file mode 100644 index 0000000000..1cb0f0e961 --- /dev/null +++ b/src/revolution/nand/NANDErrorMessage.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include + +#define REGION_DEFAULT 0 +#define REGION_EUROPE 1 +#define REGION_CHNKOR 2 + +const char* const __NANDMaxBlocksErrorMessageDefault[] = { + "\n\n\nWii本体保存メモリの空き容量が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.", + "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.", + "\n\n\nIl n'y a pas assez d'espace libre\ndans la m駑oire de la console Wii.\nVeuillez vous r馭駻er au Mode d'emploi\nde la Wii pour plus de d騁ails.", + "\n\n\nNo queda espacio libre en la memoria\nde la consola Wii. Consulta el manual\nde operaciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nNon c'\xE8 pi\xF9 spazio libero nella memoria\ndella console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.", + "\n\n\nEr is geen vrije ruimte meer in het\ninterne geheugen van het Wii-systeem.\nLees de handleiding voor meer informatie.", +}; + +const char* const __NANDMaxBlocksErrorMessageEurope[] = { + "\n\n\nWii本体保存メモリの空き容量が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nThere is no more available space in\nthe Wii System Memory. Please refer to\nthe Wii Operations Manual for details.", + "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.", + "\n\n\nIl n'y a pas assez d'espace libre dans\nla m駑oire de la console Wii. Veuillez\nvous r馭駻er au mode d'emploi Wii pour\nplus de d騁ails.", + "\n\n\nNo queda espacio libre en la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nNon c'\xE8 pi\xF9 spazio libero nella memoria\ndella console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.", + "\n\n\nEr is geen vrije ruimte meer in het\ninterne geheugen van het Wii-systeem.\nLees de handleiding voor meer informatie.", +}; + +const char* const __NANDMaxBlocksErrorMessageChinaKorea[] = { + "\n\nエラーコード405。\n\nWii本体保存メモリの空き容量が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\nError #405,\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.", +}; + +const char* const __NANDMaxFilesErrorMessageDefault[] = { + "\n\n\nWii本体保存メモリの空きファイル数が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.", + "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.", + "\n\n\nIl n'y a pas assez d'espace libre dans\nla m駑oire de la console Wii. Veuillez\nvous r馭駻er au Mode d'emploi de la Wii\npour plus de d騁ails.", + "\n\n\nNo queda espacio libre en la memoria de\nla consola Wii. Consulta el manual de\noperaciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nImpossibile salvare altri dati nella\nmemoria della console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.", + "\n\n\nEr is geen ruimte meer beschikbaar\nin het interne geheugen van het\nWii-systeem. Lees de handleiding voor\nmeer informatie.", +}; + +const char* const __NANDMaxFilesErrorMessageEurope[] = { + "\n\n\nWii本体保存メモリの空きファイル数が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nThere is no more available space in\nthe Wii System Memory. Please refer to\nthe Wii Operations Manual for details.", + "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.", + "\n\n\nIl n'y a pas assez d'espace libre dans\nla m駑oire de la console Wii. Veuillez\nvous r馭駻er au mode d'emploi Wii pour\nplus de d騁ails.", + "\n\n\nNo queda espacio libre en la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nImpossibile salvare altri dati nella\nmemoria della console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.", + "\n\n\nEr is geen ruimte meer beschikbaar\nin het interne geheugen van het\nWii-systeem. Lees de handleiding voor\nmeer informatie.", +}; + +const char* const __NANDMaxFilesErrorMessageChinaKorea[] = { + "\n\nエラーコード406。\n\nWii本体保存メモリの空きファイル数が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\nError #406,\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.", +}; + +const char* const __NANDCorruptErrorMessageDefault[] = { + "\n\n\nWii本体保存メモリが壊れました。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nThe Wii system memory has been damaged.\nRefer to the Wii Operations Manual for\ndetails.", + "\n\n\nDer Speicher der Wii-Konsole\nist besch臈igt. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.", + "\n\n\nLa m駑oire de la console Wii a 騁\xE9 \nendommag馥. Veuillez vous r馭駻er au\nMode d'emploi de la Wii pour plus de\nd騁ails.", + "\n\n\nLa memoria de la consola Wii\nest\xE1 da\xF1""ada. Consulta el manual de\noperaciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nLa memoria della console Wii e\ndanneggiata. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.", + "\n\n\nHet interne geheugen van het\nWii-systeem is beschadigd. Lees de\nWii-handleiding voor meer informatie.", +}; + +const char* const __NANDCorruptErrorMessageEurope[] = { + "\n\n\nWii本体保存メモリが壊れました。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nThe Wii System Memory has been damaged.\nPlease refer to the Wii Operations Manual\nfor details.", + "\n\n\nDer Speicher der Wii-Konsole\nist beschadigt. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.", + "\n\n\nLa m駑oire de la console Wii est\nendommag馥. Veuillez vous r馭駻er au\nmode d'emploi Wii pour plus de d騁ails.\n", + "\n\n\nLa memoria de la consola Wii est\xE1 da\xF1""ada.\nConsulta el manual de instrucciones de la\nconsola Wii para obtener m疽 informaci\xF3n.", + "\n\n\nLa memoria della console Wii e\ndanneggiata. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.", + "\n\n\nHet interne geheugen van het\nWii-systeem is beschadigd. Lees de\nWii-handleiding voor meer informatie.", +}; + +const char* const __NANDCorruptErrorMessageChinaKorea[] = { + "\n\nエラーコード408。\n\nWii本体保存メモリが壊れました。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\nError #408,\n\nThe Wii system memory has been damaged.\nRefer to the Wii Operations Manual for\ndetails.", +}; + +const char* const __NANDBusyErrorMessageDefault[] = { + "\n\n\nWii本体保存メモリの書き込み/読み出しが\nできませんでした。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nCould not access Wii system memory.\nRefer to the Wii Operations Manual for\ndetails.", + "\n\n\nAuf den Speicher der Wii-Konsole konnte\nnicht zugegriffen werden. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.", + "\n\n\nImpossible d'acc馘er \xE0 la m駑oire de\nla console Wii. Veuillez vous r馭駻er\nau Mode d'emploi de la Wii pour plus\nde d騁ails.", + "\n\n\nNo se ha podido acceder a la memoria de\nla consola Wii. Consulta el manual de\noperaciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nImpossibile accedere alla memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.", + "\n\n\nHet interne geheugen van het Wii-systeem\nkan niet worden gelezen of beschreven.\nLees de Wii-handleiding voor meer\ninformatie.", +}; + +const char* const __NANDBusyErrorMessageEurope[] = { + "\n\n\nWii本体保存メモリの書き込み/読み出しが\nできませんでした。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nCould not access the Wii System Memory.Please refer to the Wii Operations Manual\nfor details.", + "\n\n\nAuf den Speicher der Wii-Konsole konnte\nnicht zugegriffen werden. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.", + "\n\n\nImpossible d'acc馘er \xE0 la m駑oire de la\nconsole Wii. Veuillez vous r馭駻er au\nmode d'emploi Wii pour plus de d騁ails.", + "\n\n\nNo se ha podido acceder a la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nImpossibile accedere alla memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.", + "\n\n\nHet interne geheugen van het Wii-systeem\nkan niet worden gelezen of beschreven.\nLees de Wii-handleiding voor meer\ninformatie.", +}; + +const char* const __NANDBusyErrorMessageChinaKorea[] = { + "\n\nエラーコード411。\n\nWii本体保存メモリの書き込み/読み出しが\nできませんでした。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\nError #411,\n\nCould not access Wii system memory.\nRefer to the Wii Operations Manual for\ndetails.", +}; + +const char* const __NANDUnknownErrorMessageDefault[] = { + "\n\n\nWii本体保存メモリの書き込み/読み出し中に\nエラーが発生しました。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nAn error occurred while accessing Wii\nsystem memory. Refer to the Wii\nOperations Manual for details.", + "\n\n\nBeim Zugriff auf den Speicher der\nWii-Konsole ist ein Fehler aufgetreten.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.", + "\n\n\nUne erreur est survenue pendant le\nprocessus de lecture ou d'馗riture\ndans la m駑oire de la console Wii.\nVeuillez vous r馭駻er au Mode d'emploi\nde la Wii pour plus de d騁ails.", + "\n\n\nSe ha producido un error al intentar\nacceder a la memoria de la consola Wii.\nConsulta el manual de operaciones\nde la consola Wii para obtener m疽\ninformaci\xF3n.", + "\n\n\nSi \xE8 verificato un errore durante la\nlettura o la modifica dei dati\nall'interno della memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.", + "\n\n\nEr is een fout opgetreden tijdens het\nlezen of beschrijven van het interne\ngeheugen van het Wii-systeem. Lees de\nWii-handleiding voor meer informatie.", +}; + +const char* const __NANDUnknownErrorMessageEurope[] = { + "\n\n\nWii本体保存メモリの書き込み/読み出し中に\nエラーが発生しました。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\n\nAn error occurred during the process of\nreading from or writing to the Wii System\nMemory. Please refer to the Wii Operations\nManual for details.", + "\n\n\nBeim Zugriff auf den Speicher der\nWii-Konsole ist ein Fehler aufgetreten.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.", + "\n\n\nUne erreur est survenue avec la m駑oire\nde la console Wii pendant le processus\nde lecture ou d'馗riture. Veuillez vous\nr馭駻er au mode d'emploi Wii pour plus\nde d鑼ails.", + "\n\n\nSe ha producido un error durante la\nlectura o escritura de la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.", + "\n\n\nSi \xE8 verificato un errore durante la\nlettura o la modifica dei dati\nall'interno della memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.", + "\n\n\nEr is een fout opgetreden tijdens het\nlezen of beschrijven van het interne\ngeheugen van het Wii-systeem. Lees de\nWii-handleiding voor meer informatie.", +}; + +const char* const __NANDUnknownErrorMessageChinaKorea[] = { + "\n\nエラーコード412。\n\nWii本体保存メモリの書き込み/読み出し中に\nエラーが発生しました。\nくわしくはWii本体の取扱説明書をお読み\nください。", + "\n\nError #412,\n\nAn error occurred while accessing Wii\nsystem memory. Refer to the Wii\nOperations Manual for details.", +}; + +static void (*NANDErrorFunc)(s32 errorCode); + +const char* const* __NANDSetErrorMessageList(s32 errorCode, s32 region) { + u8 i = 0; + struct { + int errorCode; + int region; + const char* const* messageList; + } errorMessageList[] = { + {ISFS_ERROR_MAXBLOCKS, REGION_DEFAULT, __NANDMaxBlocksErrorMessageDefault}, + {ISFS_ERROR_MAXFILES, REGION_DEFAULT, __NANDMaxFilesErrorMessageDefault}, + {ISFS_ERROR_CORRUPT, REGION_DEFAULT, __NANDCorruptErrorMessageDefault}, + {ISFS_ERROR_BUSY, REGION_DEFAULT, __NANDBusyErrorMessageDefault}, + {IOS_ERROR_FAIL_ALLOC, REGION_DEFAULT, __NANDBusyErrorMessageDefault}, + {ISFS_ERROR_UNKNOWN, REGION_DEFAULT, __NANDUnknownErrorMessageDefault}, + {IOS_ERROR_UNKNOWN, REGION_DEFAULT, __NANDUnknownErrorMessageDefault}, + + {ISFS_ERROR_MAXBLOCKS, REGION_EUROPE, __NANDMaxBlocksErrorMessageEurope}, + {ISFS_ERROR_MAXFILES, REGION_EUROPE, __NANDMaxFilesErrorMessageEurope}, + {ISFS_ERROR_CORRUPT, REGION_EUROPE, __NANDCorruptErrorMessageEurope}, + {ISFS_ERROR_BUSY, REGION_EUROPE, __NANDBusyErrorMessageEurope}, + {IOS_ERROR_FAIL_ALLOC, REGION_EUROPE, __NANDBusyErrorMessageEurope}, + {ISFS_ERROR_UNKNOWN, REGION_EUROPE, __NANDUnknownErrorMessageEurope}, + {IOS_ERROR_UNKNOWN, REGION_EUROPE, __NANDUnknownErrorMessageEurope}, + + {ISFS_ERROR_MAXBLOCKS, REGION_CHNKOR, __NANDMaxBlocksErrorMessageChinaKorea}, + {ISFS_ERROR_MAXFILES, REGION_CHNKOR, __NANDMaxFilesErrorMessageChinaKorea}, + {ISFS_ERROR_CORRUPT, REGION_CHNKOR, __NANDCorruptErrorMessageChinaKorea}, + {ISFS_ERROR_BUSY, REGION_CHNKOR, __NANDBusyErrorMessageChinaKorea}, + {IOS_ERROR_FAIL_ALLOC, REGION_CHNKOR, __NANDBusyErrorMessageChinaKorea}, + {ISFS_ERROR_UNKNOWN, REGION_CHNKOR, __NANDUnknownErrorMessageChinaKorea}, + {IOS_ERROR_UNKNOWN, REGION_CHNKOR, __NANDUnknownErrorMessageChinaKorea}, + }; + + while (i < 21) { + if (region == errorMessageList[i].region) { + if (errorCode == errorMessageList[i].errorCode) { + return errorMessageList[i].messageList; + } + i++; + continue; + } + + i = i + 7; + } + + return __NANDUnknownErrorMessageDefault; +} + +void __NANDShowErrorMessage(s32 errorCode) { + const char* message; + const char* const* messageList; + GXColor bg = { 0, 0, 0, 0 }; + GXColor fg = { 255, 255, 255, 0 }; + + if (SCGetLanguage() == SC_LANG_JAPANESE) { + OSSetFontEncode(OS_FONT_ENCODE_SJIS); + } else { + OSSetFontEncode(OS_FONT_ENCODE_ANSI); + } + + switch (SCGetProductGameRegion()) { + case 0: + case 1: + case 3: + default: + messageList = __NANDSetErrorMessageList(errorCode, REGION_DEFAULT); + break; + case 2: + messageList = __NANDSetErrorMessageList(errorCode, REGION_EUROPE); + break; + case 4: + case 5: + messageList = __NANDSetErrorMessageList(errorCode, REGION_CHNKOR); + break; + } + + if (SCGetLanguage() > SC_LANG_DUTCH) { + message = messageList[1]; + } else { + message = messageList[SCGetLanguage()]; + } + + OSFatal(fg, bg, message); +} + +BOOL NANDSetAutoErrorMessaging(BOOL show) { + BOOL enabled = OSDisableInterrupts(); + BOOL var_r30 = NANDErrorFunc != NULL ? TRUE : FALSE; + + NANDErrorFunc = show ? __NANDShowErrorMessage : NULL; + + OSRestoreInterrupts(enabled); + return var_r30; +} + +void __NANDPrintErrorMessage(s32 errorCode) { + if (NANDErrorFunc) { + NANDErrorFunc(errorCode); + } +} diff --git a/src/revolution/nand/NANDLogging.c b/src/revolution/nand/NANDLogging.c new file mode 100644 index 0000000000..7d0d862c81 --- /dev/null +++ b/src/revolution/nand/NANDLogging.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include + +static IOSFd s_fd = -255; +static IOSError s_err = ISFS_ERROR_UNKNOWN; +static int s_stage; +static char s_message[256] ATTRIBUTE_ALIGN(64); +static NANDLoggingCallback s_callback = 0; + +static void asyncRoutine(ISFSError, void*); +static void prepareLine(char line[256], int, const char*); + +BOOL reserveFileDescriptor(void) { + BOOL enabled = FALSE; + BOOL busy_flag = FALSE; + + enabled = OSDisableInterrupts(); + + if (s_fd == -255) { + s_fd = -254; + busy_flag = FALSE; + } else if (s_fd == -254) { + busy_flag = TRUE; + } else if (s_fd >= 0) { + busy_flag = TRUE; + } else { + busy_flag = TRUE; + } + + OSRestoreInterrupts(enabled); + return busy_flag ? FALSE : TRUE; +} + +BOOL NANDLoggingAddMessageAsync(NANDLoggingCallback cb, s32 errorCode, const char* fmt, ...) { + va_list ap; + ISFSError err = ISFS_ERROR_UNKNOWN; + + if (!reserveFileDescriptor()) { + return FALSE; + } + + va_start(ap, fmt); + vsnprintf(s_message, 256, fmt, ap); + va_end(ap); + + s_callback = cb; + s_stage = 1; + + if (errorCode == ISFS_ERROR_UNKNOWN || errorCode == IOS_ERROR_UNKNOWN) { + s_err = errorCode; + } + + err = ISFS_OpenAsync((const u8*)"/shared2/test2/nanderr.log", 3, asyncRoutine, 0); + + if (err == ISFS_ERROR_OK) { + return TRUE; + } else { + return FALSE; + } +} + +static void callbackRoutine(BOOL result) { + if (s_callback) { + s_callback(result, s_err); + } +} + +static void asyncRoutine(ISFSError result, void *ctxt) { + ISFSError ret = ISFS_ERROR_UNKNOWN; + static char s_rBuf[256] ATTRIBUTE_ALIGN(64); + static char s_wBuf[256] ATTRIBUTE_ALIGN(64); + ++s_stage; + + if (s_stage == 2) { + if (result >= 0) { + s_fd = result; + ret = ISFS_SeekAsync(s_fd, 0, 0, asyncRoutine, 0); + + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 3) { + if (result == 0) { + ret = ISFS_ReadAsync(s_fd, (u8*)s_rBuf, 256, asyncRoutine, 0); + + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 4) { + if (result == 256) { + ret = ISFS_SeekAsync(s_fd, 0, 0, asyncRoutine, 0); + + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 5) { + if (result == 0) { + int n = 0; + s_rBuf[255] = '\0'; + n = atoi(s_rBuf); + prepareLine(s_wBuf, n, s_message); + ret = ISFS_WriteAsync(s_fd, (const u8*)s_wBuf, 256, asyncRoutine, 0); + + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 6) { + if (result == 256) { + int n = atoi(s_rBuf); + ret = ISFS_SeekAsync(s_fd, n * 256, 0, asyncRoutine, 0); + + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 7) { + int n = atoi(s_rBuf); + if (result == n * 256) { + ret = ISFS_WriteAsync(s_fd, (const u8*)s_wBuf, 256, asyncRoutine, 0); + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 8) { + if (result == 256) { + ret = ISFS_CloseAsync(s_fd, asyncRoutine, 0); + + if (ret != ISFS_ERROR_OK) { + callbackRoutine(FALSE); + } + } else { + callbackRoutine(FALSE); + } + } else if (s_stage == 9) { + if (result == ISFS_ERROR_OK) { + s_fd = -255; + callbackRoutine(TRUE); + } else { + callbackRoutine(FALSE); + } + } +} + +static void prepareLine(char line[256], int n, const char* msg) { + char titleID[64]; + int end = 0; + struct OSCalendarTime cal; + + memset(line, ' ', 254); + OSTicksToCalendarTime(OSGetTime(), &cal); + strncpy(titleID, nandGetHomeDir() + 7, 8 + 1 + 8); + titleID[8] = '-'; + titleID[8 + 1 + 8] = '\0'; + end = snprintf(line, 256, "%d %04d/%02d/%02d %02d:%02d:%02d %s %s", n % (64 - 1) + 1, cal.year, cal.mon + 1, cal.mday, cal.hour, cal.min, cal.sec, titleID, msg); + + if (end < 256) { + line[end] = ' '; + } + + line[254] = '\r'; + line[255] = '\n'; +} diff --git a/src/revolution/nand/NANDOpenClose.c b/src/revolution/nand/NANDOpenClose.c new file mode 100644 index 0000000000..cd3ed01142 --- /dev/null +++ b/src/revolution/nand/NANDOpenClose.c @@ -0,0 +1,683 @@ +#include +#include +#include + +static void nandOpenCallback(ISFSError, void *); +static void nandReadCloseCallback(ISFSError, void *); +static void nandCloseCallback(ISFSError, void *); +static void nandSafeCloseCallback(ISFSError, void *); +static void nandSafeOpenCallback(const ISFSError, void *); +void nandReadOpenCallback(ISFSError, void *); +s32 nandSafeCloseAsync(NANDFileInfo *, NANDCallback, NANDCommandBlock *, BOOL); +s32 nandSafeOpenAsync(const char *, NANDFileInfo *, const u8, void *, const u32, NANDCallback, NANDCommandBlock *, BOOL, BOOL); +s32 nandSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, BOOL privilege_flag, BOOL simple_flag); +s32 nandSafeClose(NANDFileInfo* info, BOOL simple_flag); +static ISFSError nandCopy(s32 fd, s32 origFd, void* buf, const u32 length); +static u32 nandGetUniqueNumber(void); + +IOSFd nandOpen(const char* path, const u8 accType, NANDCommandBlock* block, const BOOL async_flag, const BOOL privilege_flag) { + IOSFd fd = ISFS_ERROR_UNKNOWN; + char absPath[64] = ""; + u32 access = 0; + + ASSERTMSGLINE(188, path, "NULL pointer is detected."); + ASSERTMSGLINE(192, accType == 1 || accType == 2 || accType == 3, "Access type is illegal."); + + nandGenerateAbsPath(absPath, path); + + if (!privilege_flag && nandIsPrivatePath(absPath)) { + return ISFS_ERROR_ACCESS; + } else { + switch (accType) { + case 3: + access = 3; + break; + case 1: + access = 1; + break; + case 2: + access = 2; + break; + default: + break; + } + + if (async_flag) { + fd = ISFS_OpenAsync((const u8*)absPath, access, nandOpenCallback, block); + } else { + fd = ISFS_Open((const u8*)absPath, access); + } + + return fd; + } +} + +s32 NANDOpen(const char* path, NANDFileInfo* info, const u8 accType) { + IOSFd fd = ISFS_ERROR_UNKNOWN; + + ASSERTMSGLINE(234, info, "NULL pointer is detected."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + fd = nandOpen(path, accType, NULL, FALSE, FALSE); + + if (fd >= 0) { + info->fileDescriptor = fd; + info->mark = 1; + return NAND_RESULT_OK; + } else { + return nandConvertErrorCode(fd); + } +} + +s32 NANDPrivateOpen(const char* path, NANDFileInfo* info, const u8 accType) { + IOSFd fd = ISFS_ERROR_UNKNOWN; + + ASSERTMSGLINE(259, info, "NULL pointer is detected."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + fd = nandOpen(path, accType, NULL, FALSE, TRUE); + + if (fd >= 0) { + info->fileDescriptor = fd; + info->mark = 1; + return NAND_RESULT_OK; + } else { + return nandConvertErrorCode(fd); + } +} + +s32 NANDOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, NANDCallback cb, NANDCommandBlock* block) { + IOSFd fd = ISFS_ERROR_UNKNOWN; + + ASSERTMSGLINE(284, info, "NULL pointer is detected."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + block->fileInfo = info; + fd = nandOpen(path, accType, block, TRUE, FALSE); + return nandConvertErrorCode(fd); +} + +s32 NANDPrivateOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, NANDCallback cb, NANDCommandBlock* block) { + IOSFd fd = ISFS_ERROR_UNKNOWN; + + ASSERTMSGLINE(301, info, "NULL pointer is detected."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + block->fileInfo = info; + fd = nandOpen(path, accType, block, TRUE, TRUE); + return nandConvertErrorCode(fd); +} + +void nandOpenCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result >= 0) { + ((NANDFileInfo*)(b->fileInfo))->fileDescriptor = result; + ((NANDFileInfo*)(b->fileInfo))->stage = 2; + ((NANDFileInfo*)(b->fileInfo))->mark = 1; + ((NANDCallback)(b->callback))(NAND_RESULT_OK, b); + } else { + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); + } +} + +s32 NANDClose(NANDFileInfo* info) { + ISFSError err = ISFS_ERROR_UNKNOWN; + + ASSERTMSGLINE(341, info, "NULL pointer is detected."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + if (info->mark != 1) { + return NAND_RESULT_INVALID; + } + + err = ISFS_Close(info->fileDescriptor); + + if (err == ISFS_ERROR_OK) { + info->mark = 2; + } + + return nandConvertErrorCode(err); +} + +s32 NANDCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block) { + ISFSError err = ISFS_ERROR_UNKNOWN; + + ASSERTMSGLINE(364, info, "NULL pointer is detected."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + if (info->mark != 1) { + return NAND_RESULT_INVALID; + } + + block->callback = cb; + block->fileInfo = info; + err = ISFS_CloseAsync(info->fileDescriptor, nandCloseCallback, block); + return nandConvertErrorCode(err); +} + +s32 NANDSimpleSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length) { + return nandSafeOpen(path, info, accType, buf, length, FALSE, TRUE); +} + +s32 nandSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, BOOL privilege_flag, BOOL simple_flag) { + ASSERTMSGLINE(411, path, "NULL pointer detected.\n"); + ASSERTMSGLINE(412, info, "NULL pointer detected.\n"); + ASSERTMSGLINE(413, accType == 1 || accType == 2 || accType == 3, "Illegal access type.\n"); + ASSERTMSGLINE(414, buf, "NULL pointer detected.\n"); + ASSERTMSGLINE(415, !((u32)buf & 0x1F), "32byte alignment is required.\n"); + ASSERTMSGLINE(416, !(length & 0x1F), "Buffer size must be multiples of 32.\n"); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + if (simple_flag && ((length & 0x3FFF) != 0)) { + return NAND_RESULT_INVALID; + } + + info->accType = accType; + info->stage = 0; + nandGenerateAbsPath(info->origPath, path); + + if (!privilege_flag && nandIsPrivatePath(info->origPath)) { + return NAND_RESULT_ACCESS; + } + + if (accType == 1) { + IOSFd fd = ISFS_Open((u8*)(info->origPath), 1); + if (fd >= ISFS_ERROR_OK) { + info->fileDescriptor = fd; + info->stage = 2; + + if (!simple_flag) { + info->mark = 3; + } else { + info->mark = 5; + } + + return NAND_RESULT_OK; + } else { + return nandConvertErrorCode(fd); + } + } else if (accType == 2 || accType == 3) { + char dirName[64]; + char relName[13] = {0}; + ISFSError ret = ISFS_ERROR_UNKNOWN; + IOSUid ownerId; + IOSGid groupId; + u32 attr; + u32 ownerAcc; + u32 groupAcc; + u32 othersAcc; + u32 sp48 = -1; + + ret = ISFS_CreateDir((u8*)"/tmp/sys", 0, 3, 3, 3); + if (ret != ISFS_ERROR_OK && ret != ISFS_ERROR_EXISTS) { + return nandConvertErrorCode(ret); + } else { + info->stage = 1; + ret = ISFS_GetAttr((const u8*)info->origPath, &ownerId, &groupId, &attr, &ownerAcc, &groupAcc, &othersAcc); + if (ret != ISFS_ERROR_OK) { + return nandConvertErrorCode(ret); + } + + info->origFd = ISFS_Open((u8*)info->origPath, 1); + if (info->origFd < 0) { + return nandConvertErrorCode(info->origFd); + } + + info->stage = 2; + + if (!simple_flag) { + sp48 = nandGetUniqueNumber(); + sprintf(dirName, "%s/%08x", "/tmp/sys", sp48); + ret = ISFS_CreateDir((const u8*)dirName, 0, 3, 0, 0); + if (ret != 0) { + return nandConvertErrorCode(ret); + } + + info->stage = 3; + } + + nandGetRelativeName(relName, info->origPath); + + if (!simple_flag) { + sprintf(info->tmpPath, "%s/%08x/%s", "/tmp/sys", sp48, relName); + } else { + sprintf(info->tmpPath, "%s/%s", "/tmp/sys", relName); + } + + ret = ISFS_CreateFile((const u8*)info->tmpPath, attr, ownerAcc, groupAcc, othersAcc); + if (ret != 0) { + return nandConvertErrorCode(ret); + } + + info->stage = 4; + + if (accType == 2) { + info->fileDescriptor = ISFS_Open((const u8*)info->tmpPath, 2); + } else if (accType == 3) { + info->fileDescriptor = ISFS_Open((const u8*)info->tmpPath, 3); + } + + if (info->fileDescriptor < 0) { + return nandConvertErrorCode(info->fileDescriptor); + } + + info->stage = 5; + + ret = nandCopy(info->fileDescriptor, info->origFd, buf, length); + if (ret != 0) { + return nandConvertErrorCode(ret); + } + + ret = ISFS_Seek(info->fileDescriptor, 0, 0); + if (ret == 0) { + ret = 0; + } else { + return nandConvertErrorCode(ret); + } + + if (ret == 0) { + if (simple_flag) { + info->mark = 5; + } else { + info->mark = 3; + } + } + + return nandConvertErrorCode(ret); + } + } else { + return NAND_RESULT_INVALID; + } +} + +s32 NANDSimpleSafeClose(NANDFileInfo* info) { + return nandSafeClose(info, TRUE); +} + +s32 nandSafeClose(NANDFileInfo* info, BOOL simple_flag) { + ISFSError err = ISFS_ERROR_UNKNOWN; + char tmpdir[64] = ""; + ASSERTMSGLINE(612, info, "NULL pointer detected.\n"); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + if (!((info->mark == 3 && !simple_flag) || (info->mark == 5 && simple_flag))) { + return NAND_RESULT_INVALID; + } + + if (info->accType == 1) { + err = ISFS_Close(info->fileDescriptor); + if (err == 0) { + info->stage = 7; + + if (!simple_flag) { + info->mark = 4; + } else { + info->mark = 6; + } + } + + return nandConvertErrorCode(err); + } else if (info->accType == 2 || info->accType == 3) { + err = ISFS_Close(info->fileDescriptor); + if (err != 0) { + return nandConvertErrorCode(err); + } + + info->stage = 6; + + err = ISFS_Close(info->origFd); + if (err != 0) { + return nandConvertErrorCode(err); + } + + info->stage = 7; + + err = ISFS_Rename((const u8*)info->tmpPath, (const u8*)info->origPath); + if (err != 0) { + return nandConvertErrorCode(err); + } + + info->stage = 8; + + if (!simple_flag) { + nandGetParentDirectory(tmpdir, info->tmpPath); + err = ISFS_Delete((const u8*)tmpdir); + if (err == 0) { + info->stage = 9; + info->mark = 4; + } + } else { + info->mark = 6; + } + + return nandConvertErrorCode(err); + } else { + OSReport("Illegal NANDFileInfo.\n"); + } + + return NAND_RESULT_INVALID; +} + +s32 NANDPrivateSafeOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block) { + return nandSafeOpenAsync(path, info, accType, buf, length, cb, block, TRUE, FALSE); +} + +s32 nandSafeOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block, BOOL privilege_flag, BOOL simple_flag) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + if (simple_flag && ((length & 0x3FFF) != 0)) { + return NAND_RESULT_INVALID; + } + + info->accType = accType; + info->stage = 0; + block->simpleFlag = simple_flag; + nandGenerateAbsPath(info->origPath, path); + + if (!privilege_flag && nandIsPrivatePath(info->origPath)) { + return NAND_RESULT_ACCESS; + } + + if (accType == 1) { + IOSFd fd = -1; + block->fileInfo = info; + block->callback = cb; + fd = ISFS_OpenAsync((u8*)(info->origPath), 1, nandReadOpenCallback, block); + + if (fd == ISFS_ERROR_OK) { + return NAND_RESULT_OK; + } else { + return nandConvertErrorCode(fd); + } + } else if (accType == 2 || accType == 3) { + ISFSError ret = ISFS_ERROR_UNKNOWN; + block->fileInfo = info; + block->callback = cb; + block->state = 0; + block->copyBuf = buf; + block->bufLength = length; + ret = ISFS_CreateDirAsync((u8*)"/tmp/sys", 0, 3, 3, 3, nandSafeOpenCallback, block); + + if (ret == ISFS_ERROR_OK) { + return NAND_RESULT_OK; + } else { + return nandConvertErrorCode(ret); + } + } else { + return NAND_RESULT_INVALID; + } +} + +void nandSafeOpenCallback(const ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result >= 0 || (result == ISFS_ERROR_EXISTS && b->state == 0)) { + NANDFileInfo* info = b->fileInfo; + ISFSError ret = ISFS_ERROR_UNKNOWN; + + if (b->state == 0) { + info->stage = 1; + } + + if (b->state == 2) { + info->origFd = result; + info->stage = 2; + } + + if (b->state == 2 && b->simpleFlag) { + b->state += 2; + } else { + ++b->state; + } + + if (b->state == 1) { + ret = ISFS_GetAttrAsync((u8*)(info->origPath), &b->ownerId, &b->groupId, &b->attr, &b->ownerAcc, &b->groupAcc, &b->othersAcc, nandSafeOpenCallback, ctxt); + } else if (b->state == 2) { + ret = ISFS_OpenAsync((u8*)(info->origPath), 1, nandSafeOpenCallback, ctxt); + } else if (b->state == 3) { + char tmpDir[64]; + b->uniqNo = nandGetUniqueNumber(); + sprintf(tmpDir, "%s/%08x", "/tmp/sys", b->uniqNo); + ret = ISFS_CreateDirAsync((u8*)tmpDir, 0, 3, 0, 0, nandSafeOpenCallback, ctxt); + } else if (b->state == 4) { + char filename[13]; + nandGetRelativeName(filename, info->origPath); + + if (!b->simpleFlag) { + info->stage = 3; + sprintf(info->tmpPath, "%s/%08x/%s", "/tmp/sys", b->uniqNo, filename); + } + else { + sprintf(info->tmpPath, "%s/%s", "/tmp/sys", filename); + } + + ret = ISFS_CreateFileAsync((u8*)info->tmpPath, b->attr, b->ownerAcc, b->groupAcc, b->othersAcc, nandSafeOpenCallback, ctxt); + } else if (b->state == 5) { + info->stage = 4; + + if (info->accType == 2) { + ret = ISFS_OpenAsync((u8*)info->tmpPath, 2, nandSafeOpenCallback, ctxt); + } else if (info->accType == 3) { + ret = ISFS_OpenAsync((u8*)info->tmpPath, 3, nandSafeOpenCallback, ctxt); + } else { + ret = ISFS_ERROR_UNKNOWN; + } + } else if (b->state == 6) { + info->fileDescriptor = result; + info->stage = 5; + b->state = 7; + ret = ISFS_ReadAsync(info->origFd, b->copyBuf, b->bufLength, nandSafeOpenCallback, ctxt); + } else if (b->state == 7) { + ret = ISFS_ReadAsync(info->origFd, b->copyBuf, b->bufLength, nandSafeOpenCallback, ctxt); + } else if (b->state == 8) { + if (result > 0) { + b->state = 6; + ret = ISFS_WriteAsync(info->fileDescriptor, b->copyBuf, (u32)result, nandSafeOpenCallback, ctxt); + } else if (result == 0) { + ret = ISFS_SeekAsync(info->fileDescriptor, 0, 0, nandSafeOpenCallback, ctxt); + } + } else if (b->state == 9) { + if (result == 0) { + if (!b->simpleFlag) { + info->mark = 3; + } else { + info->mark = 5; + } + + ((NANDCallback)(b->callback))(nandConvertErrorCode(ISFS_ERROR_OK), b); + } else { + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); + } + return; + } + + if (ret != ISFS_ERROR_OK) { + ((NANDCallback)(b->callback))(nandConvertErrorCode(ret), b); + } + } else { + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); + } +} + +void nandReadOpenCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result >= 0) { + ((NANDFileInfo*)(b->fileInfo))->fileDescriptor = result; + ((NANDFileInfo*)(b->fileInfo))->stage = 2; + + if (!(b->simpleFlag)) { + ((NANDFileInfo*)(b->fileInfo))->mark = 3; + } else { + ((NANDFileInfo*)(b->fileInfo))->mark = 5; + } + + ((NANDCallback)(b->callback))(NAND_RESULT_OK, b); + } else { + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); + } +} + +void dummyStrings() { + OSReport("NULL pointer detected."); +} + +s32 NANDSafeCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block) { + return nandSafeCloseAsync(info, cb, block, FALSE); +} + +s32 nandSafeCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block, BOOL simple_flag) { + ISFSError err = ISFS_ERROR_UNKNOWN; + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + if (!((info->mark == 3 && !simple_flag) || (info->mark == 5 && simple_flag))) { + return NAND_RESULT_INVALID; + } + + block->simpleFlag = simple_flag; + if (info->accType == 1) { + block->fileInfo = info; + block->callback = cb; + err = ISFS_CloseAsync(info->fileDescriptor, nandReadCloseCallback, block); + } else if (info->accType == 2 || info->accType == 3) { + block->fileInfo = info; + block->callback = cb; + block->state = 10; + err = ISFS_CloseAsync(info->fileDescriptor, nandSafeCloseCallback, block); + } else { + err = ISFS_ERROR_INVALID; + } + + return nandConvertErrorCode(err); +} + +void nandSafeCloseCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result == 0) { + NANDFileInfo* info = b->fileInfo; + ISFSError ret = ISFS_ERROR_UNKNOWN; + + if (b->state == 12) { + info->stage = 8; + } + + if (b->state == 12 && b->simpleFlag) { + b->state += 2; + } else { + ++b->state; + } + + if (b->state == 11) { + info->stage = 6; + ret = ISFS_CloseAsync(info->origFd, nandSafeCloseCallback, ctxt); + } else if (b->state == 12) { + info->stage = 7; + ret = ISFS_RenameAsync((u8*)(info->tmpPath), (u8*)(info->origPath), nandSafeCloseCallback, ctxt); + } else if (b->state == 13) { + char tmpdir[64] = ""; + nandGetParentDirectory(tmpdir, info->tmpPath); + ret = ISFS_DeleteAsync((u8*)tmpdir, nandSafeCloseCallback, ctxt); + } else if (b->state == 14) { + if (!(b->simpleFlag)) { + info->stage = 9; + } + + info->mark = 4; + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); + return; + } + + if (ret != 0) { + ((NANDCallback)(b->callback))(nandConvertErrorCode(ret), b); + } + } else { + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); + } +} + +static void nandReadCloseCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result == 0) { + ((NANDFileInfo*)(b->fileInfo))->stage = 7; + ((NANDFileInfo*)(b->fileInfo))->mark = 4; + } + + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); +} + +static void nandCloseCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + + if (result == 0) { + ((NANDFileInfo*)(b->fileInfo))->stage = 7; + ((NANDFileInfo*)(b->fileInfo))->mark = 2; + } + + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); +} + +static ISFSError nandCopy(s32 fd, s32 origFd, void* buf, const u32 length) { + while (1) { + ISFSError ret; + ISFSError ret2; + + ret = ISFS_Read(origFd, buf, length); + if (ret == 0) { + return 0; + } + + if (ret < 0) { + return ret; + } + + ret2 = ISFS_Write(fd, buf, ret); + if (ret2 < 0) { + return ret2; + } + + if (ret2 != ret) {} + } +} + +static u32 nandGetUniqueNumber(void) { + static u32 s_counter = 0; + u32 ret; + BOOL enabled = OSDisableInterrupts(); + ret = s_counter++; + OSRestoreInterrupts(enabled); + return ret; +} diff --git a/src/revolution/nand/nand.c b/src/revolution/nand/nand.c new file mode 100644 index 0000000000..c81f226f84 --- /dev/null +++ b/src/revolution/nand/nand.c @@ -0,0 +1,565 @@ +#include +#include +#include + +#include + +static BOOL nandInspectPermission(u8); +static void nandSplitPerm(u8, u32*, u32*, u32*); +static void nandGetTypeCallback(ISFSError, void*); +static void nandGetStatusCallback(ISFSError, void*); +BOOL nandCheckPathName(const char* path); +BOOL nandCheckCharacter(char character); +void nandCallback(ISFSError, void*); + +ISFSError nandCreate(const char* path, const u8 perm, const u8 attr, NANDCommandBlock* block, BOOL async_flag, + BOOL privilege_flag) { + char absPath[64] = ""; + u32 owner = 0, group = 0, others = 0; + + ASSERTMSGLINE(254, path, "NULL pointer is detected."); + + if (!nandCheckPathName(path)) { + return ISFS_ERROR_INVALID; + } + + nandGenerateAbsPath(absPath, path); + + if (!privilege_flag && nandIsPrivatePath(absPath)) { + return ISFS_ERROR_ACCESS; + } else if (!nandInspectPermission(perm)) { + return ISFS_ERROR_INVALID; + } else { + nandSplitPerm(perm, &owner, &group, &others); + + if (async_flag) { + return ISFS_CreateFileAsync((const u8*)absPath, attr, owner, group, others, nandCallback, block); + } else { + return ISFS_CreateFile((const u8*)absPath, attr, owner, group, others); + } + } +} + +s32 NANDCreate(const char* path, const u8 perm, const u8 attr) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandCreate(path, perm, attr, NULL, FALSE, FALSE)); +} + +s32 NANDPrivateCreate(const char* path, u8 perm, u8 attr) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandCreate(path, perm, attr, NULL, FALSE, TRUE)); +} + +s32 NANDPrivateCreateAsync(const char* path, u8 perm, u8 attr, NANDCallback cb, + NANDCommandBlock* block) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(nandCreate(path, perm, attr, block, TRUE, TRUE)); +} + +static ISFSError nandDelete(const char* path, NANDCommandBlock* block, + BOOL async_flag, BOOL privilege_flag) { + char absPath[64] = ""; + nandGenerateAbsPath(absPath, path); + + if (!privilege_flag && nandIsPrivatePath(absPath)) { + return ISFS_ERROR_ACCESS; + } else { + if (async_flag) { + return ISFS_DeleteAsync((const u8*)absPath, nandCallback, block); + } else { + return ISFS_Delete((const u8*)absPath); + } + } +} + +s32 NANDDelete(const char* path) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandDelete(path, NULL, FALSE, FALSE)); +} + +s32 NANDPrivateDelete(const char* path) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandDelete(path, NULL, FALSE, TRUE)); +} + +s32 NANDPrivateDeleteAsync(const char* path, NANDCallback cb, NANDCommandBlock* block) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(nandDelete(path, block, TRUE, TRUE)); +} + +s32 NANDRead(NANDFileInfo* info, void* buf, const u32 length) { + ASSERTMSGLINE(412, info, "*info is NULL pointer!"); + ASSERTMSGLINE(413, buf, "*buf is NULL pointer!"); + ASSERTMSGLINE(414, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned."); + ASSERTMSGLINE(415, !(length & 0x1F), "Buffer length must be multiples of 32 bytes."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(ISFS_Read(info->fileDescriptor, (u8*)buf, length)); +} + +s32 NANDReadAsync(NANDFileInfo* info, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block) { + ASSERTMSGLINE(428, info, "*info is NULL pointer!"); + ASSERTMSGLINE(429, buf, "*buf is NULL pointer!"); + ASSERTMSGLINE(430, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned."); + ASSERTMSGLINE(431, !(length & 0x1F), "Buffer length must be multiples of 32 bytes."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(ISFS_ReadAsync(info->fileDescriptor, (u8*)buf, length, nandCallback, block)); +} + +s32 NANDWrite(NANDFileInfo* info, const void* buf, const u32 length) { + ASSERTMSGLINE(448, info, "*info is NULL pointer!"); + ASSERTMSGLINE(449, buf, "*buf is NULL pointer!"); + ASSERTMSGLINE(450, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(ISFS_Write(info->fileDescriptor, (const u8*)buf, length)); +} + +s32 NANDWriteAsync(NANDFileInfo* info, const void* buf, const u32 length, + NANDCallback cb, NANDCommandBlock* block) { + ASSERTMSGLINE(463, info, "*info is NULL pointer!"); + ASSERTMSGLINE(464, buf, "*buf is NULL pointer!"); + ASSERTMSGLINE(465, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(ISFS_WriteAsync(info->fileDescriptor, (const u8*)buf, length, nandCallback, block)); +} + +static ISFSError nandSeek(const IOSFd fd, const s32 offset, const s32 whence, + NANDCommandBlock* block, const BOOL async_flag) { + u32 w = 0xFFFFFFFF; + + switch (whence) { + case 0: + w = 0; + break; + case 1: + w = 1; + break; + case 2: + w = 2; + break; + default: + break; + } + + if (async_flag) { + return ISFS_SeekAsync(fd, offset, w, nandCallback, block); + } else { + return ISFS_Seek(fd, offset, w); + } +} + +s32 NANDSeek(NANDFileInfo* info, const s32 offset, const s32 whence) { + ASSERTMSGLINE(513, info, "*info is NULL pointer!"); + ASSERTMSGLINE(514, whence == 0 || whence == 1 || whence == 2, "Illegal whence parameter."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandSeek(info->fileDescriptor, offset, whence, NULL, FALSE)); +} + +s32 NANDSeekAsync(NANDFileInfo* info, const s32 offset, const s32 whence, + NANDCallback cb, NANDCommandBlock* block) { + ASSERTMSGLINE(527, info, "*info is NULL pointer!"); + ASSERTMSGLINE(528, whence == 0 || whence == 1 || whence == 2, "Illegal whence parameter."); + + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(nandSeek(info->fileDescriptor, offset, whence, block, TRUE)); +} + +ISFSError nandCreateDir(const char* path, const u8 perm, const u8 attr, + NANDCommandBlock* block, const BOOL async_flag, + const BOOL privilege_flag) { + char absPath[64] = ""; + + if (!nandCheckPathName(path)) { + return ISFS_ERROR_INVALID; + } + + nandGenerateAbsPath(absPath, path); + if (!privilege_flag && nandIsPrivatePath(absPath)) { + return ISFS_ERROR_ACCESS; + } else if (!nandInspectPermission(perm)) { + return ISFS_ERROR_INVALID; + } else { + u32 owner = 0; + u32 group = 0; + u32 others = 0; + nandSplitPerm(perm, &owner, &group, &others); + + if (async_flag) { + return ISFS_CreateDirAsync((const u8*)absPath, attr, owner, group, others, nandCallback, block); + } else { + return ISFS_CreateDir((const u8*)absPath, attr, owner, group, others); + } + } +} + +s32 NANDPrivateCreateDir(const char* path, u8 perm, u8 attr) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandCreateDir(path, perm, attr, NULL, FALSE, TRUE)); +} + +s32 NANDPrivateCreateDirAsync(const char* path, u8 perm, u8 attr, + NANDCallback cb, NANDCommandBlock* block) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + return nandConvertErrorCode(nandCreateDir(path, perm, attr, block, TRUE, TRUE)); +} + +ISFSError nandMove(const char* path, const char* destDir, + NANDCommandBlock* block, const BOOL async_flag, + const BOOL privilege_flag) { + char absOldPath[64] = ""; + char absNewPath[64] = ""; + char relativeName[13] = ""; + + nandGenerateAbsPath(absOldPath, path); + nandGetRelativeName(relativeName, absOldPath); + nandGenerateAbsPath(absNewPath, destDir); + + if (strcmp(absNewPath, "/") == 0) { + sprintf(absNewPath, "/%s", relativeName); + } else { + strcat(absNewPath, "/"); + strcat(absNewPath, relativeName); + } + + if (!privilege_flag && (nandIsPrivatePath(absOldPath) || nandIsPrivatePath(absNewPath))) { + return ISFS_ERROR_ACCESS; + } else { + if (async_flag) { + return ISFS_RenameAsync((const u8*)absOldPath, (const u8*)absNewPath, nandCallback, block); + } else { + return ISFS_Rename((const u8*)absOldPath, (const u8*)absNewPath); + } + } +} + +s32 NANDMove(const char* path, const char* destDir) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandMove(path, destDir, NULL, FALSE, FALSE)); +} + +static ISFSError nandGetFileStatus(IOSFd fd, u32* length, u32* pos) { + ISFSFileStats fstat ATTRIBUTE_ALIGN(32); + ISFSError result = ISFS_GetFileStats(fd, &fstat); + if (result == ISFS_ERROR_OK) { + if (length) { + *length = fstat.size; + } + if (pos) { + *pos = fstat.offset; + } + } + + return result; +} + +s32 NANDGetLength(NANDFileInfo* info, u32* length) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandGetFileStatus(info->fileDescriptor, length, NULL)); +} + +void nandGetFileStatusAsyncCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + ISFSFileStats* fstat = (ISFSFileStats*)OSRoundUp32B((u32)(b->absPath)); + + if (result == ISFS_ERROR_OK) { + if (b->length) { + *(b->length) = fstat->size; + } + if (b->pos) { + *(b->pos) = fstat->offset; + } + } + + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); +} + +static ISFSError nandGetFileStatusAsync(IOSFd fd, NANDCommandBlock* block) { + ISFSError result = ISFS_ERROR_UNKNOWN; + ISFSFileStats* fstat = (ISFSFileStats*)OSRoundUp32B((u32)(block->absPath)); + return ISFS_GetFileStatsAsync(fd, fstat, nandGetFileStatusAsyncCallback, block); +} + +s32 NANDGetLengthAsync(NANDFileInfo* info, u32* length, NANDCallback cb, NANDCommandBlock* block) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + block->length = length; + block->pos = NULL; + return nandConvertErrorCode(nandGetFileStatusAsync(info->fileDescriptor, block)); +} + +void dummyStrings() { + OSReport("NULL pointer is detected.\n"); + OSReport("NAND library internal error: bytes value must be illegal."); + OSReport("NAND library internal error: inodes value must be illegal."); + OSReport("NAND library internal error: length value must be illegal."); + OSReport("NULL pointer detected."); +} + +void nandComposePerm(u8* perm, const u32 ownerAcc, const u32 groupAcc, const u32 othersAcc) { + u32 p = 0; + + if (ownerAcc & 1) { + p = p | 0x10; + } + + if (ownerAcc & 2) { + p = p | 0x20; + } + + if (groupAcc & 1) { + p = p | 4; + } + + if (groupAcc & 2) { + p = p | 8; + } + + if (othersAcc & 1) { + p = p | 1; + } + + if (othersAcc & 2) { + p = p | 2; + } + + ASSERTMSGLINE(1007, (p & ~0xFF) == 0, "NAND library internal error.\n"); + *perm = (u8)p; +} + +static void nandSplitPerm(u8 perm, u32* ownerAcc, u32* groupAcc, u32* othersAcc) { + *ownerAcc = 0; + *groupAcc = 0; + *othersAcc = 0; + + if (perm & 0x10) { + *ownerAcc = *ownerAcc | 1; + } + + if (perm & 0x20) { + *ownerAcc = *ownerAcc | 2; + } + + if (perm & 4) { + *groupAcc = *groupAcc | 1; + } + + if (perm & 8) { + *groupAcc = *groupAcc | 2; + } + + if (perm & 1) { + *othersAcc = *othersAcc | 1; + } + + if (perm & 2) { + *othersAcc = *othersAcc | 2; + } +} + +ISFSError nandGetStatus(const char* path, NANDStatus* stat, + NANDCommandBlock* block, const BOOL async_flag, + const BOOL privilege_flag) { + char absPath[64] = ""; + + ASSERTMSGLINE(1052, path, "NULL pointer detected."); + ASSERTMSGLINE(1053, stat, "NULL pointer detected."); + + nandGenerateAbsPath(absPath, path); + if (!privilege_flag && nandIsUnderPrivatePath(absPath)) { + return ISFS_ERROR_ACCESS; + } else { + if (async_flag) { + return ISFS_GetAttrAsync( + (const u8*)absPath, &(stat->ownerId), &(stat->groupId), + &(block->attr), &(block->ownerAcc), &(block->groupAcc), + &(block->othersAcc), nandGetStatusCallback, block); + } else { + u32 attr = 0, ownerAcc = 0, groupAcc = 0, othersAcc = 0; + ISFSError result = + ISFS_GetAttr((const u8*)absPath, &stat->ownerId, &stat->groupId, + &attr, &ownerAcc, &groupAcc, &othersAcc); + if (result == ISFS_ERROR_OK) { + nandComposePerm(&stat->permission, ownerAcc, groupAcc, othersAcc); + stat->attribute = (u8)attr; + } + + return result; + } + } +} + +void nandGetStatusCallback(ISFSError result, void* ctxt) { + NANDCommandBlock* b = (NANDCommandBlock*)ctxt; + if (result == ISFS_ERROR_OK) { + NANDStatus* stat = (NANDStatus*)(b->status); + stat->attribute = (u8)(b->attr); + nandComposePerm(&(stat->permission), b->ownerAcc, b->groupAcc, + b->othersAcc); + } + ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b); +} + +s32 NANDGetStatus(const char* path, NANDStatus* stat) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandGetStatus(path, stat, NULL, FALSE, FALSE)); +} + +s32 NANDPrivateGetStatus(const char* path, NANDStatus* stat) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + return nandConvertErrorCode(nandGetStatus(path, stat, NULL, FALSE, TRUE)); +} + +s32 NANDPrivateGetStatusAsync(const char* path, NANDStatus* stat, + NANDCallback cb, NANDCommandBlock* block) { + if (!nandIsInitialized()) { + return NAND_RESULT_FATAL_ERROR; + } + + block->callback = cb; + block->status = stat; + return nandConvertErrorCode(nandGetStatus(path, stat, block, TRUE, TRUE)); +} + +void NANDSetUserData(NANDCommandBlock* block, void* data) { + block->userData = data; +} + +void* NANDGetUserData(const NANDCommandBlock* block) { + return block->userData; +} + +static BOOL nandInspectPermission(const u8 perm) { + if (perm & 0x10) { + return TRUE; + } else { + return FALSE; + } +} + +BOOL nandCheckPathName(const char* path) { + char absNewPath[64]; + char relativeName[13]; + int len = -1; + + ASSERTMSGLINE(1266, path, "null pointer is detected in argument of path\n"); + + len = strlen(path); + if (len == 0) { + return 0; + } else { + u32 i; + if (nandIsRelativePath(path)) { + for (i = 0; i < len; i++) { + if (!nandCheckCharacter(path[i])) { + return 0; + } + } + } else { + strcpy(absNewPath, path); + if (absNewPath[len - 1] == '/' && len - 1 != 0) { + absNewPath[len - 1] = 0; + } + + nandGetRelativeName(relativeName, absNewPath); + len = strlen(relativeName); + + for (i = 0; i < len; i++) { + if (!nandCheckCharacter(relativeName[i])) { + return 0; + } + } + } + } + + return 1; +} + +BOOL nandCheckCharacter(char character) { + u32 i; + const char legal_characters[66] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_./"; + + for (i = 0; i < sizeof(legal_characters); i++) { + if (character == legal_characters[i]) { + return TRUE; + } + } + + return FALSE; +} + +void dummyStrings2() { + OSReport("/tmp/count_tmpdir"); + OSReport("countfile"); + OSReport("*number is NULL pointer!"); + OSReport("*cleaning is NULL pointer!"); + OSReport("NAND library internal error: free_inodes value must be illegal."); + OSReport("%s/%s_%.2d"); + OSReport("count_tmp.count must be less than ISFS_FD_ENTRIES.!"); +} diff --git a/src/revolution/os/OS.c b/src/revolution/os/OS.c index fc769082da..006870ca43 100644 --- a/src/revolution/os/OS.c +++ b/src/revolution/os/OS.c @@ -4,16 +4,17 @@ #include #include #include +#include #include "__os.h" +#include "__dvd.h" + +#include #define NOP 0x60000000 // external functions extern void EnableMetroTRKInterrupts(void); -extern void __OSInitMemoryProtection(void); -extern void IPCCltInit(void); -extern BOOL __DVDCheckDevice(void); #define DB_EXCEPTIONRET_OFFSET 0xC #define DB_EXCEPTIONDEST_OFFSET 0x8 diff --git a/src/revolution/os/OSAlarm.c b/src/revolution/os/OSAlarm.c index fd50cb3199..235ce0d18d 100644 --- a/src/revolution/os/OSAlarm.c +++ b/src/revolution/os/OSAlarm.c @@ -16,7 +16,7 @@ static void DecrementerExceptionCallback(__REGISTER __OSException exception, __R static void DecrementerExceptionHandler(__OSException exception, OSContext* context); static BOOL OnReset(BOOL final, u32 event); -static OSShutdownFunctionInfo ResetFunctionInfo = {OnReset, 0xFFFFFFFF, NULL, NULL}; +static OSShutdownFunctionInfo ShutdownFunctionInfo = {OnReset, 0xFFFFFFFF, NULL, NULL}; static OSAlarmQueue AlarmQueue; #define ASSERTREPORT(line, cond) \ @@ -52,7 +52,7 @@ void __OSInitAlarm(void) { if (__OSGetExceptionHandler(8) != DecrementerExceptionHandler) { AlarmQueue.head = AlarmQueue.tail = NULL; __OSSetExceptionHandler(8, DecrementerExceptionHandler); - OSRegisterShutdownFunction(&ResetFunctionInfo); + OSRegisterShutdownFunction(&ShutdownFunctionInfo); } } diff --git a/src/revolution/os/OSAudioSystem.c b/src/revolution/os/OSAudioSystem.c index d9e1df68c9..a16bf21954 100644 --- a/src/revolution/os/OSAudioSystem.c +++ b/src/revolution/os/OSAudioSystem.c @@ -1,6 +1,8 @@ #include #include +#include + #include "__os.h" // prototypes diff --git a/src/revolution/os/OSExec.c b/src/revolution/os/OSExec.c index b4b8b2d6cb..50441daab8 100644 --- a/src/revolution/os/OSExec.c +++ b/src/revolution/os/OSExec.c @@ -1,13 +1,14 @@ #include #include #include +#include +#include #include "__os.h" #include "__dvd.h" // extern extern size_t wcslen(const wchar_t *); -extern int __DVDLayoutFormat; extern volatile u32 BOOT_REGION_START AT_ADDRESS(0x812FDFF0); extern volatile u32 BOOT_REGION_END AT_ADDRESS(0x812FDFEC); diff --git a/src/revolution/os/OSInterrupt.c b/src/revolution/os/OSInterrupt.c index cc5e30cb1c..6250a07300 100644 --- a/src/revolution/os/OSInterrupt.c +++ b/src/revolution/os/OSInterrupt.c @@ -1,5 +1,6 @@ #include #include +#include #include "__os.h" diff --git a/src/revolution/os/OSLaunch.c b/src/revolution/os/OSLaunch.c index ce81619f6b..77bc9f8572 100644 --- a/src/revolution/os/OSLaunch.c +++ b/src/revolution/os/OSLaunch.c @@ -1,5 +1,6 @@ #include #include +#include #include "__os.h" diff --git a/src/revolution/os/OSLink.c b/src/revolution/os/OSLink.c index 7ec245ae58..4abc2f64c2 100644 --- a/src/revolution/os/OSLink.c +++ b/src/revolution/os/OSLink.c @@ -1,5 +1,6 @@ #include #include +#include #include "__os.h" diff --git a/src/revolution/os/OSNet.c b/src/revolution/os/OSNet.c index be1f33f184..e68c789734 100644 --- a/src/revolution/os/OSNet.c +++ b/src/revolution/os/OSNet.c @@ -1,6 +1,8 @@ #include #include -#include +#include + +#include "__os.h" static OSShutdownFunctionInfo ShutdownFuncInfo; @@ -8,21 +10,6 @@ static IOSFd nwc24ShtFd = -1; static int nwc24ShtRetryRest = 0; static BOOL NWC24iIsRequestPending = FALSE; -typedef enum NWC24Err { - NWC24_OK = 0, -} NWC24Err; - -NWC24Err NWC24iPrepareShutdown(void); -NWC24Err NWC24iSynchronizeRtcCounter(BOOL); -NWC24Err NWC24iOpenResourceManager_(const char* callerName, const char* path, IOSFd* fd, u32 flags); -NWC24Err NWC24SuspendScheduler(void); -NWC24Err NWC24iSetRtcCounter_(int rtc, BOOL); -NWC24Err NWC24iIoctlResourceManager_(const char* callerName, IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen); -NWC24Err NWC24iCloseResourceManager_(const char* callerName, IOSFd); -NWC24Err NWC24iRequestShutdown(u32 event, s32* result); -NWC24Err NWC24iIoctlResourceManagerAsync_(const char* callerName, IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen, void* cbArg); -BOOL NWC24iIsAsyncRequestPending_(void); - BOOL NWC24Shutdown_(BOOL, u32); static void REXInit(void); diff --git a/src/revolution/os/OSPlayTime.c b/src/revolution/os/OSPlayTime.c index 9eddeb8b48..78a10b2ca8 100644 --- a/src/revolution/os/OSPlayTime.c +++ b/src/revolution/os/OSPlayTime.c @@ -2,6 +2,10 @@ #include #include #include +#include +#include + +#include "__os.h" OSAlarm __OSExpireAlarm; OSTime __OSExpireTime; diff --git a/src/revolution/os/OSReset.c b/src/revolution/os/OSReset.c index 12cd1ae138..1685475a18 100644 --- a/src/revolution/os/OSReset.c +++ b/src/revolution/os/OSReset.c @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include "__os.h" #include "__dvd.h" diff --git a/src/revolution/os/OSStateFlags.c b/src/revolution/os/OSStateFlags.c index 5bb81fbcd6..da4b199cd5 100644 --- a/src/revolution/os/OSStateFlags.c +++ b/src/revolution/os/OSStateFlags.c @@ -1,6 +1,6 @@ #include #include -#include +#include static OSStateFlags StateFlags ATTRIBUTE_ALIGN(32); diff --git a/src/revolution/os/OSStateTM.c b/src/revolution/os/OSStateTM.c index 1b4e491e67..3961ff7d98 100644 --- a/src/revolution/os/OSStateTM.c +++ b/src/revolution/os/OSStateTM.c @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -29,6 +30,7 @@ static void __OSDefaultResetCallback(void); static void __OSDefaultPowerCallback(void); static void __OSRegisterStateEvent(void); +static int AccessVIDimRegs(void); static void LockUp(void); OSPowerCallback OSSetPowerCallback(OSPowerCallback callback) { diff --git a/src/revolution/os/OSSync.c b/src/revolution/os/OSSync.c index 7bf90140c2..094efbe1ba 100644 --- a/src/revolution/os/OSSync.c +++ b/src/revolution/os/OSSync.c @@ -1,5 +1,6 @@ #include #include +#include #include "__os.h" diff --git a/src/revolution/os/OSThread.c b/src/revolution/os/OSThread.c index 6bf9b1a6c3..dbe745d17a 100644 --- a/src/revolution/os/OSThread.c +++ b/src/revolution/os/OSThread.c @@ -542,7 +542,7 @@ void OSCancelThread(OSThread* thread) { int OSJoinThread(OSThread* thread, void* val) { BOOL enabled = OSDisableInterrupts(); - ASSERTMSG1LINE(LINE(1061, 1092, 1092), __OSIsThreadActive(thread) != 0, "OSJoinThread(): thread %p is not active.", thread); + ASSERTMSG1LINE(1061, __OSIsThreadActive(thread) != 0, "OSJoinThread(): thread %p is not active.", thread); if (!(thread->attr & 1) && (thread->state != OS_THREAD_STATE_MORIBUND) && (thread->queueJoin.head == NULL)) { OSSleepThread(&thread->queueJoin); @@ -851,7 +851,7 @@ void OSSetThreadSpecific(s32 index, void* ptr) { OSThread* thread; thread = __OSCurrentThread; - ASSERTLINE(LINE(1573, 1604, 1604), 0 <= index && index < OS_THREAD_SPECIFIC_MAX); + ASSERTLINE(1573, 0 <= index && index < OS_THREAD_SPECIFIC_MAX); if (thread != 0 && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) { thread->specific[index] = ptr; @@ -862,7 +862,7 @@ void* OSGetThreadSpecific(s32 index) { OSThread* thread; thread = __OSCurrentThread; - ASSERTLINE(LINE(1584, 1615, 1615), 0 <= index && index < OS_THREAD_SPECIFIC_MAX); + ASSERTLINE(1584, 0 <= index && index < OS_THREAD_SPECIFIC_MAX); if (thread != 0 && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) { return thread->specific[index]; diff --git a/src/revolution/os/__os.h b/src/revolution/os/__os.h index 75e76f5df3..3c41d2158a 100644 --- a/src/revolution/os/__os.h +++ b/src/revolution/os/__os.h @@ -13,11 +13,7 @@ extern char* __OSExceptionNames[17]; // D ONLY u32 __OSIsDebuggerPresent(void); void __OSPSInit(void); -void __OSInitIPCBuffer(void); -void __OSInitSTM(void); -void __OSInitNet(void); -void __OSInitPlayTime(void); -void __OSStartPlayRecord(void); +void __OSGetIOSRev(OSIOSRev* rev); // OSAlarm void __OSInitAlarm(void); @@ -44,6 +40,7 @@ void __OSGetExecParams(OSExecParams* params); void __OSSetExecParams(const OSExecParams* params, OSExecParams* addr); void __OSBootDolSimple(u32 doloffset, u32 restartCode, void* regionStart, void* regionEnd, BOOL argsUseDefault, s32 argc, char** argv); void __OSBootDol(u32 doloffset, u32 restartCode, const char** argv); +void __OSLaunchMenu(); extern u32 __OSNextPartitionType; // OSInterrupt @@ -62,25 +59,37 @@ OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask global); void __OSDispatchInterrupt(__OSException exception, OSContext* context); void __OSModuleInit(void); +// OSIpc +void __OSInitIPCBuffer(void); + +// OSLaunch +void __OSRelaunchTitle(u32 resetCode); + // OSMemory void __OSInitMemoryProtection(void); +void __OSRestoreCodeExecOnMEM1(u32 param_0); // OSMutex void __OSUnlockAllMutex(OSThread* thread); int __OSCheckDeadLock(OSThread* thread); int __OSCheckMutexes(OSThread* thread); +// OSNet +void __OSInitNet(void); + // OSPlayTime -void __OSGetPlayTime(ESTicketView* ticket, __OSPlayTimeType* type, u32* playTime); +void __OSInitPlayTime(void); +s32 __OSGetPlayTime(ESTicketView* ticket, __OSPlayTimeType* type, u32* playTime); +BOOL __OSWriteExpiredFlagIfSet(void); + +// OSPlayRecord +void __OSStartPlayRecord(void); // OSReset -void __OSDoHotReset(u32 resetCode); void __OSShutdownDevices(u32 event); int __OSCallShutdownFunctions(BOOL final, u32 event); - -// OSResetSW -void __OSResetSWInterruptHandler(s16 exception, OSContext* context); -void __OSSetResetButtonTimer(u8 min); +void __OSReturnToMenuForError(void); +void __OSHotResetForError(void); // OSRtc int __OSGetRTC(u32* rtc); @@ -96,6 +105,8 @@ int __OSReadROM(void* buffer, s32 length, s32 offset); int __OSReadROMAsync(void* buffer, s32 length, s32 offset, void (*callback)()); u8 __OSGetBootMode(void); void __OSSetBootMode(u8 ntd); +BOOL __OSGetRTCFlags(u32* flags); +BOOL __OSClearRTCFlags(void); // OSSync extern void __OSSystemCallVectorStart(); @@ -103,6 +114,14 @@ extern void __OSSystemCallVectorEnd(); void __OSInitSystemCall(void); +// OSStateTM +void __OSInitSTM(void); +BOOL __OSWriteExpiredFlag(void); +int __OSSetVIForceDimming(BOOL isEnabled, u32 yShift, u32 xShift); +s32 __OSUnRegisterStateEvent(void); +void __OSShutdownToSBY(void); +void __OSHotReset(void); + // OSThread void __OSThreadInit(void); s32 __OSGetEffectivePriority(OSThread* thread); @@ -137,12 +156,6 @@ OSTime __get_clock(void); u32 __get_time(void); int __to_gm_time(void); - -// unsorted -void __OSWriteExpiredFlag(void); -void __OSReturnToMenuForError(void); -void __OSHotResetForError(void); - #ifdef __cplusplus } #endif diff --git a/src/revolution/pad/Pad.c b/src/revolution/pad/Pad.c new file mode 100644 index 0000000000..81105f2b8f --- /dev/null +++ b/src/revolution/pad/Pad.c @@ -0,0 +1,894 @@ +#include +#include +#include + +#include + +#include "__si.h" + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifdef SDK_AUG2010 +#define BUILD_DATE "Aug 23 2010" +#if DEBUG +#define BUILD_TIME "17:27:55" +#else +#define BUILD_TIME "17:33:06" +#endif +#elif SDK_SEP2006 +#define BUILD_DATE "Sep 21 2006" +#define BUILD_TIME "14:32:13" +#endif + +#ifdef SDK_AUG2010 +#if DEBUG +const char* __PADVersion = "<< RVL_SDK - PAD \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>"; +#else +const char* __PADVersion = "<< RVL_SDK - PAD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>"; +#endif +#elif SDK_SEP2006 +const char* __PADVersion = "<< RVL_SDK - PAD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>"; +#endif + +#define PAD_ALL \ + ( \ + PAD_BUTTON_LEFT | \ + PAD_BUTTON_RIGHT | \ + PAD_BUTTON_DOWN | \ + PAD_BUTTON_UP | \ + PAD_TRIGGER_Z | \ + PAD_TRIGGER_R | \ + PAD_TRIGGER_L | \ + PAD_BUTTON_A | \ + PAD_BUTTON_B | \ + PAD_BUTTON_X | \ + PAD_BUTTON_Y | \ + PAD_BUTTON_MENU | \ + 0x2000 | \ + 0x0080 \ + ) + +static s32 ResettingChan = 0x20; +static u32 XPatchBits = PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT; +static u32 AnalogMode = 0x300; +static u32 Spec = PAD_SPEC_5; + +static BOOL Initialized; +static u32 EnabledBits; +static u32 ResettingBits; +static u32 RecalibrateBits; +static u32 WaitingBits; +static u32 CheckingBits; +static u32 PendingBits; +static u32 BarrelBits; + +static u32 Type[4]; +static PADStatus Origin[4]; + +u32 __PADSpec; + +// prototypes +static void PADTypeAndStatusCallback(s32 chan, u32 type); +static u16 GetWirelessID(s32 chan); +static void SetWirelessID(s32 chan, u16 id); +static void DoReset(); +static void PADEnable(s32 chan); +static void ProbeWireless(s32 chan); +static void PADProbeCallback(s32 chan, u32 error, OSContext *context); +static void PADDisable(s32 chan); +static void UpdateOrigin(s32 chan); +static void PADOriginCallback(s32 chan, u32 error, OSContext *context); +static void PADFixCallback(s32 unused, u32 error, struct OSContext *context); +static void PADResetCallback(s32 unused, u32 error, struct OSContext *context); +static void PADReceiveCheckCallback(s32 chan, u32 error); +static void SPEC0_MakeStatus(s32 chan, PADStatus *status, u32 data[2]); +static void SPEC1_MakeStatus(s32 chan, PADStatus *status, u32 data[2]); +static s8 ClampS8(s8 var, s8 org); +static u8 ClampU8(u8 var, u8 org); +static void SPEC2_MakeStatus(s32 chan, PADStatus *status, u32 data[2]); +static BOOL OnShutdown(BOOL f, u32); +void __PADDisableXPatch(void); +BOOL __PADDisableRumble(BOOL disable); + +typedef void (*SPECCallback)(s32, PADStatus*, u32*); +static SPECCallback MakeStatus = SPEC2_MakeStatus; + +static u32 CmdTypeAndStatus; +static u32 CmdReadOrigin = 0x41000000; +static u32 CmdCalibrate = 0x42000000; +static u32 CmdProbeDevice[4]; + +static OSShutdownFunctionInfo ShutdownFunctionInfo = { + OnShutdown, + 127, + NULL, + NULL, +}; + +static void PADEnable(s32 chan) { + u32 cmd; + u32 chanBit; + u32 data[2]; + + chanBit = PAD_CHAN0_BIT >> chan; + EnabledBits |= chanBit; + SIGetResponse(chan, &data); + cmd = (AnalogMode | 0x400000); + SISetCommand(chan, cmd); + SIEnablePolling(EnabledBits); +} + +static void PADDisable(s32 chan) { + BOOL enabled; + u32 chanBit; + + enabled = OSDisableInterrupts(); + chanBit = PAD_CHAN0_BIT >> chan; + SIDisablePolling(chanBit); + EnabledBits &= ~chanBit; + WaitingBits &= ~chanBit; + CheckingBits &= ~chanBit; + PendingBits &= ~chanBit; + BarrelBits &= ~chanBit; + OSSetWirelessID(chan, 0); + OSRestoreInterrupts(enabled); +} + +static void DoReset() { + u32 chanBit; + + ResettingChan = __cntlzw(ResettingBits); + if (ResettingChan != 32) { + ASSERTLINE(589, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN); + chanBit = (PAD_CHAN0_BIT >> ResettingChan); + ResettingBits &= ~chanBit; + + memset(&Origin[ResettingChan], 0, sizeof(PADStatus)); + SIGetTypeAsync(ResettingChan, PADTypeAndStatusCallback); + } +} + +static void UpdateOrigin(s32 chan) { + PADStatus* origin; + u32 chanBit = PAD_CHAN0_BIT >> chan; + + origin = &Origin[chan]; + switch (AnalogMode & 0x00000700u) { + case 0x00000000u: + case 0x00000500u: + case 0x00000600u: + case 0x00000700u: + origin->triggerLeft &= ~15; + origin->triggerRight &= ~15; + origin->analogA &= ~15; + origin->analogB &= ~15; + break; + case 0x00000100u: + origin->substickX &= ~15; + origin->substickY &= ~15; + origin->analogA &= ~15; + origin->analogB &= ~15; + break; + case 0x00000200u: + origin->substickX &= ~15; + origin->substickY &= ~15; + origin->triggerLeft &= ~15; + origin->triggerRight &= ~15; + break; + case 0x00000300u: break; + case 0x00000400u: break; + } + + origin->stickX -= 128; + origin->stickY -= 128; + origin->substickX -= 128; + origin->substickY -= 128; + + if (XPatchBits & chanBit) { + if (64 < origin->stickX && (SIGetType(chan) & 0xFFFF0000) == SI_GC_CONTROLLER) { + origin->stickX = 0; + } + } +} + +static void PADOriginCallback(s32 chan, u32 error, OSContext* context) { + ASSERTLINE(671, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN); + ASSERTLINE(672, chan == ResettingChan); + + if (!(error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))) + { + UpdateOrigin(ResettingChan); + PADEnable(ResettingChan); + } + + DoReset(); +} + +static void PADOriginUpdateCallback(s32 chan, u32 error, OSContext* context) { + ASSERTLINE(701, 0 <= chan && chan < SI_MAX_CHAN); + if (!(EnabledBits & (PAD_CHAN0_BIT >> chan))) + return; + if (!(error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))) + UpdateOrigin(chan); + if (error & SI_ERROR_NO_RESPONSE) { + PADDisable(chan); + } +} + +static void PADProbeCallback(s32 chan, u32 error, OSContext* context) { + u32 type; + ASSERTLINE(740, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN); + ASSERTLINE(741, chan == ResettingChan); + ASSERTLINE(743, (Type[chan] & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT && !(Type[chan] & SI_WIRELESS_LITE)); + + if (!(error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))) + { + PADEnable(ResettingChan); + WaitingBits |= PAD_CHAN0_BIT >> ResettingChan; + } + + DoReset(); +} + +static void PADTypeAndStatusCallback(s32 chan, u32 type) { + u32 chanBit; + u32 recalibrate; + BOOL rc = TRUE; + u32 error; + + ASSERTLINE(776, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN); + ASSERTLINE(777, chan == ResettingChan); + + chanBit = PAD_CHAN0_BIT >> ResettingChan; + error = type & 0xFF; + ASSERTLINE(786, !(error & SI_ERROR_BUSY)); + + recalibrate = RecalibrateBits & chanBit; + RecalibrateBits &= ~chanBit; + + if (error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)) + { + DoReset(); + return; + } + + type &= ~0xFF; + Type[ResettingChan] = type; + + if ((type & SI_TYPE_MASK) != SI_TYPE_GC || !(type & SI_GC_STANDARD)) { + DoReset(); + return; + } + + if (Spec < PAD_SPEC_2) { + PADEnable(ResettingChan); + DoReset(); + return; + } + + if (!(type & SI_GC_WIRELESS) || (type & SI_WIRELESS_IR)) { + if (recalibrate) { + rc = SITransfer(ResettingChan, &CmdCalibrate, 3, &Origin[ResettingChan], 10, + PADOriginCallback, 0); + } else { + rc = SITransfer(ResettingChan, &CmdReadOrigin, 1, &Origin[ResettingChan], 10, + PADOriginCallback, 0); + } + } else if ((type & SI_WIRELESS_FIX_ID) && (type & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT && + !(type & SI_WIRELESS_LITE)) + { + if (type & SI_WIRELESS_RECEIVED) { + rc = SITransfer(ResettingChan, &CmdReadOrigin, 1, &Origin[ResettingChan], 10, + PADOriginCallback, 0); + } else { + rc = SITransfer(ResettingChan, &CmdProbeDevice[ResettingChan], 3, + &Origin[ResettingChan], 8, PADProbeCallback, 0); + } + } + + if (!rc) { + PendingBits |= chanBit; + DoReset(); + return; + } +} + +static void PADReceiveCheckCallback(s32 chan, u32 type) { + u32 error; + u32 chanBit; + + chanBit = PAD_CHAN0_BIT >> chan; + + if (EnabledBits & chanBit) { + error = type & 0xFF; + type &= ~0xFF; + + WaitingBits &= ~chanBit; + CheckingBits &= ~chanBit; + + if (!(error & + (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)) && + (type & SI_GC_WIRELESS) && (type & SI_WIRELESS_FIX_ID) && (type & SI_WIRELESS_RECEIVED) && + !(type & SI_WIRELESS_IR) && (type & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT && + !(type & SI_WIRELESS_LITE)) + { + SITransfer(chan, &CmdReadOrigin, 1, &Origin[chan], 10, PADOriginUpdateCallback, 0); + } else { + PADDisable(chan); + } + } +} + +int PADReset(u32 mask) { + BOOL enabled; + u32 disableBits; + + ASSERTMSGLINE(927, !(mask & 0x0FFFFFFF), "PADReset(): invalid mask"); + + enabled = OSDisableInterrupts(); + mask |= PendingBits; + PendingBits = 0; + mask &= ~(WaitingBits | CheckingBits); + ResettingBits |= mask; + disableBits = ResettingBits & EnabledBits; + EnabledBits &= ~mask; + BarrelBits &= ~mask; + + if (Spec == 4) { + RecalibrateBits |= mask; + } + + SIDisablePolling(disableBits); + + if (ResettingChan == 0x20) { + DoReset(); + } + + OSRestoreInterrupts(enabled); + return 1; +} + +BOOL PADRecalibrate(u32 mask) { + BOOL enabled; + u32 disableBits; + + ASSERTMSGLINE(969, !(mask & 0x0FFFFFFF), "PADReset(): invalid mask"); + enabled = OSDisableInterrupts(); + + mask |= PendingBits; + PendingBits = 0; + mask &= ~(WaitingBits | CheckingBits); + ResettingBits |= mask; + disableBits = ResettingBits & EnabledBits; + EnabledBits &= ~mask; + BarrelBits &= ~mask; + + if (!(__gUnknown800030E3 & 0x40)) { + RecalibrateBits |= mask; + } + + SIDisablePolling(disableBits); + if (ResettingChan == 32) + DoReset(); + + OSRestoreInterrupts(enabled); + return 1; +} + +BOOL PADInit() { + s32 chan; + if (Initialized) { + return 1; + } + + OSRegisterVersion(__PADVersion); + + if (__PADSpec) + PADSetSpec(__PADSpec); + + Initialized = TRUE; + + if (__PADFixBits != 0) { + OSTime time = OSGetTime(); + __OSWirelessPadFixMode + = (u16)((((time)&0xffff) + ((time >> 16) & 0xffff) + ((time >> 32) & 0xffff) + ((time >> 48) & 0xffff)) + & 0x3fffu); + + RecalibrateBits = PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT; + } + + for (chan = 0; chan < SI_MAX_CHAN; ++chan) { + CmdProbeDevice[chan] = (0x4D << 24) | (chan << 22) | ((__OSWirelessPadFixMode & 0x3fffu) << 8); + } + + SIRefreshSamplingRate(); + OSRegisterShutdownFunction(&ShutdownFunctionInfo); + + return PADReset(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT); +} + +u32 PADRead(PADStatus* status) { + BOOL enabled; + s32 chan; + u32 data[2]; + u32 chanBit; + u32 sr; + int chanShift; + u32 motor; + static PADStatus pre_status[4]; + int threshold; + + threshold = 3; + enabled = OSDisableInterrupts(); + motor = 0; + + for (chan = 0; chan < 4; chan++, status++) { + chanBit = PAD_CHAN0_BIT >> chan; + chanShift = 8 * (SI_MAX_CHAN - 1 - chan); + + if (PendingBits & chanBit) { + PADReset(0); + status->err = PAD_ERR_NOT_READY; + memset(status, 0, offsetof(PADStatus, err)); + } else if ((ResettingBits & chanBit) || ResettingChan == chan) { + status->err = PAD_ERR_NOT_READY; + memset(status, 0, offsetof(PADStatus, err)); + } else if (!(EnabledBits & chanBit)) { + status->err = PAD_ERR_NO_CONTROLLER; + memset(status, 0, offsetof(PADStatus, err)); + } else if (SIIsChanBusy(chan)) { + status->err = PAD_ERR_TRANSFER; + memset(status, 0, offsetof(PADStatus, err)); + } else { + sr = SIGetStatus(chan); + if (sr & SI_ERROR_NO_RESPONSE) { + SIGetResponse(chan, data); + + if (WaitingBits & chanBit) { + status->err = PAD_ERR_NONE; + memset(status, 0, offsetof(PADStatus, err)); + + if (!(CheckingBits & chanBit)) { + CheckingBits |= chanBit; + SIGetTypeAsync(chan, PADReceiveCheckCallback); + } + } else { + PADDisable(chan); + status->err = PAD_ERR_NO_CONTROLLER; + memset(status, 0, offsetof(PADStatus, err)); + } + } else { + if (!(SIGetType(chan) & SI_GC_NOMOTOR)) { + motor |= chanBit; + } + + if (!SIGetResponse(chan, &data)) { + status->err = PAD_ERR_TRANSFER; + memset(status, 0, offsetof(PADStatus, err)); + } else if (data[0] & 0x80000000) { + status->err = PAD_ERR_TRANSFER; + memset(status, 0, offsetof(PADStatus, err)); + } else { + + + MakeStatus(chan, status, data); + + if (((Type[chan] & (0xFFFF0000)) == SI_GC_CONTROLLER) && ((status->button & 0x80) ^ 0x80)) { + threshold = 10; + } else { + threshold = 3; + } + + #ifdef __MWERKS__ + #define abs(x) __abs(x) + #else + #define abs(x) __builtin_abs(x) + #endif + + if (abs(abs(status->stickX) - abs(pre_status[chan].stickX)) >= threshold || + abs(abs(status->stickY) - abs(pre_status[chan].stickY)) >= threshold || + abs(abs(status->substickX) - abs(pre_status[chan].substickX)) >= threshold || + abs(abs(status->substickY) - abs(pre_status[chan].substickY)) >= threshold || + abs(abs(status->triggerLeft) - abs(pre_status[chan].triggerLeft)) >= threshold || + abs(abs(status->triggerRight) - abs(pre_status[chan].triggerRight)) >= threshold || + pre_status[chan].button != status->button) + { + __VIResetSIIdle(); + } + + #undef abs + + memcpy(&pre_status[chan], status, sizeof(PADStatus)); + + // Check and clear PAD_ORIGIN bit + if (status->button & 0x2000) { + status->err = PAD_ERR_TRANSFER; + memset(status, 0, offsetof(PADStatus, err)); + + // Get origin. It is okay if the following transfer fails + // since the PAD_ORIGIN bit remains until the read origin + // command complete. + SITransfer(chan, &CmdReadOrigin, 1, &Origin[chan], 10, PADOriginUpdateCallback, 0); + } else { + status->err = PAD_ERR_NONE; + + // Clear PAD_INTERFERE bit + status->button &= ~0x0080; + } + } + } + } + } + + OSRestoreInterrupts(enabled); + return motor; +} + +typedef struct XY { + u8 line; + u8 count; +} XY; + +void PADSetSamplingRate(u32 msec) { + SISetSamplingRate(msec); +} + +#if DEBUG +void __PADTestSamplingRate(u32 tvmode) { + __SITestSamplingRate(tvmode); +} +#endif + +void PADControlAllMotors(const u32* commandArray) { + BOOL enabled; + int chan; + u32 command; + BOOL commit; + u32 chanBit; + + enabled = OSDisableInterrupts(); + commit = FALSE; + + for (chan = 0; chan < SI_MAX_CHAN; chan++, commandArray++) { + chanBit = PAD_CHAN0_BIT >> chan; + if ((EnabledBits & chanBit) && !(SIGetType(chan) & 0x20000000)) { + command = *commandArray; + ASSERTMSGLINE(0x4B5, !(command & 0xFFFFFFFC), "PADControlAllMotors(): invalid command"); + if (Spec < PAD_SPEC_2 && command == PAD_MOTOR_STOP_HARD) + command = PAD_MOTOR_STOP; + if (__gUnknown800030E3 & 0x20) + command = PAD_MOTOR_STOP; + SISetCommand(chan, (0x40 << 16) | AnalogMode | (command & (0x00000001 | 0x00000002))); + commit = TRUE; + } + } + + if (commit) + SITransferCommands(); + + OSRestoreInterrupts(enabled); +} + +void PADControlMotor(s32 chan, u32 command) { + BOOL enabled; + u32 chanBit; + + ASSERTMSGLINE(1308, !(command & 0xFFFFFFFC), "PADControlMotor(): invalid command"); + + enabled = OSDisableInterrupts(); + chanBit = PAD_CHAN0_BIT >> chan; + if ((EnabledBits & chanBit) && !(SIGetType(chan) & SI_GC_NOMOTOR)) { + if (Spec < PAD_SPEC_2 && command == PAD_MOTOR_STOP_HARD) + command = PAD_MOTOR_STOP; + if (__gUnknown800030E3 & 0x20) + command = PAD_MOTOR_STOP; + SISetCommand(chan, (0x40 << 16) | AnalogMode | (command & (0x00000001 | 0x00000002))); + SITransferCommands(); + } + + OSRestoreInterrupts(enabled); +} + +void PADSetSpec(u32 spec) { + ASSERTLINE(1346, !Initialized); + __PADSpec = 0; + + switch (spec) { + case PAD_SPEC_0: + MakeStatus = SPEC0_MakeStatus; + break; + case PAD_SPEC_1: + MakeStatus = SPEC1_MakeStatus; + break; + case PAD_SPEC_2: + case PAD_SPEC_3: + case PAD_SPEC_4: + case PAD_SPEC_5: + MakeStatus = SPEC2_MakeStatus; + break; + } + Spec = spec; +} + +u32 PADGetSpec(void) { + return Spec; +} + +static void SPEC0_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) { + status->button = 0; + status->button |= ((data[0] >> 16) & 0x0008) ? PAD_BUTTON_A : 0; + status->button |= ((data[0] >> 16) & 0x0020) ? PAD_BUTTON_B : 0; + status->button |= ((data[0] >> 16) & 0x0100) ? PAD_BUTTON_X : 0; + status->button |= ((data[0] >> 16) & 0x0001) ? PAD_BUTTON_Y : 0; + status->button |= ((data[0] >> 16) & 0x0010) ? PAD_BUTTON_START : 0; + status->stickX = (s8)(data[1] >> 16); + status->stickY = (s8)(data[1] >> 24); + status->substickX = (s8)(data[1]); + status->substickY = (s8)(data[1] >> 8); + status->triggerLeft = (u8)(data[0] >> 8); + status->triggerRight = (u8)data[0]; + status->analogA = 0; + status->analogB = 0; + if (170 <= status->triggerLeft) + status->button |= PAD_TRIGGER_L; + if (170 <= status->triggerRight) + status->button |= PAD_TRIGGER_R; + status->stickX -= 128; + status->stickY -= 128; + status->substickX -= 128; + status->substickY -= 128; +} + +static void SPEC1_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) { + status->button = 0; + status->button |= ((data[0] >> 16) & 0x0080) ? PAD_BUTTON_A : 0; + status->button |= ((data[0] >> 16) & 0x0100) ? PAD_BUTTON_B : 0; + status->button |= ((data[0] >> 16) & 0x0020) ? PAD_BUTTON_X : 0; + status->button |= ((data[0] >> 16) & 0x0010) ? PAD_BUTTON_Y : 0; + status->button |= ((data[0] >> 16) & 0x0200) ? PAD_BUTTON_START : 0; + + status->stickX = (s8)(data[1] >> 16); + status->stickY = (s8)(data[1] >> 24); + status->substickX = (s8)(data[1]); + status->substickY = (s8)(data[1] >> 8); + + status->triggerLeft = (u8)(data[0] >> 8); + status->triggerRight = (u8)data[0]; + + status->analogA = 0; + status->analogB = 0; + + if (170 <= status->triggerLeft) + status->button |= PAD_TRIGGER_L; + if (170 <= status->triggerRight) + status->button |= PAD_TRIGGER_R; + + status->stickX -= 128; + status->stickY -= 128; + status->substickX -= 128; + status->substickY -= 128; +} + +static s8 ClampS8(s8 var, s8 org) { + if (0 < org) { + s8 min = (s8)(-128 + org); + if (var < min) + var = min; + } else if (org < 0) { + s8 max = (s8)(127 + org); + if (max < var) + var = max; + } + return var -= org; +} + +static u8 ClampU8(u8 var, u8 org) { + if (var < org) + var = org; + return var -= org; +} + +static void SPEC2_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) { + PADStatus* origin; + + status->button = (u16)((data[0] >> 16) & PAD_ALL); + status->stickX = (s8)(data[0] >> 8); + status->stickY = (s8)(data[0]); + + switch (AnalogMode & 0x00000700) { + case 0x00000000: + case 0x00000500: + case 0x00000600: + case 0x00000700: + status->substickX = (s8)(data[1] >> 24); + status->substickY = (s8)(data[1] >> 16); + status->triggerLeft = (u8)(((data[1] >> 12) & 0x0f) << 4); + status->triggerRight = (u8)(((data[1] >> 8) & 0x0f) << 4); + status->analogA = (u8)(((data[1] >> 4) & 0x0f) << 4); + status->analogB = (u8)(((data[1] >> 0) & 0x0f) << 4); + break; + case 0x00000100: + status->substickX = (s8)(((data[1] >> 28) & 0x0f) << 4); + status->substickY = (s8)(((data[1] >> 24) & 0x0f) << 4); + status->triggerLeft = (u8)(data[1] >> 16); + status->triggerRight = (u8)(data[1] >> 8); + status->analogA = (u8)(((data[1] >> 4) & 0x0f) << 4); + status->analogB = (u8)(((data[1] >> 0) & 0x0f) << 4); + break; + case 0x00000200: + status->substickX = (s8)(((data[1] >> 28) & 0x0f) << 4); + status->substickY = (s8)(((data[1] >> 24) & 0x0f) << 4); + status->triggerLeft = (u8)(((data[1] >> 20) & 0x0f) << 4); + status->triggerRight = (u8)(((data[1] >> 16) & 0x0f) << 4); + status->analogA = (u8)(data[1] >> 8); + status->analogB = (u8)(data[1] >> 0); + break; + case 0x00000300: + status->substickX = (s8)(data[1] >> 24); + status->substickY = (s8)(data[1] >> 16); + status->triggerLeft = (u8)(data[1] >> 8); + status->triggerRight = (u8)(data[1] >> 0); + status->analogA = 0; + status->analogB = 0; + break; + case 0x00000400: + status->substickX = (s8)(data[1] >> 24); + status->substickY = (s8)(data[1] >> 16); + status->triggerLeft = 0; + status->triggerRight = 0; + status->analogA = (u8)(data[1] >> 8); + status->analogB = (u8)(data[1] >> 0); + break; + } + + status->stickX -= 128; + status->stickY -= 128; + status->substickX -= 128; + status->substickY -= 128; + + if (((Type[chan] & (0xFFFF0000)) == SI_GC_CONTROLLER) && ((status->button & 0x80) ^ 0x80)) { + BarrelBits |= (PAD_CHAN0_BIT >> chan); + status->stickX = 0; + status->stickY = 0; + status->substickX = 0; + status->substickY = 0; + return; + } else { + BarrelBits &= ~(PAD_CHAN0_BIT >> chan); + } + + origin = &Origin[chan]; + status->stickX = ClampS8(status->stickX, origin->stickX); + status->stickY = ClampS8(status->stickY, origin->stickY); + status->substickX = ClampS8(status->substickX, origin->substickX); + status->substickY = ClampS8(status->substickY, origin->substickY); + status->triggerLeft = ClampU8(status->triggerLeft, origin->triggerLeft); + status->triggerRight = ClampU8(status->triggerRight, origin->triggerRight); +} + +int PADGetType(s32 chan, u32* type) { + u32 chanBit; + + *type = SIGetType(chan); + chanBit = PAD_CHAN0_BIT >> chan; + if (ResettingBits & chanBit || ResettingChan == chan || !(EnabledBits & chanBit)) { + return 0; + } + return 1; +} + +BOOL PADSync(void) { + return ResettingBits == 0 && (s32)ResettingChan == 32 && !SIBusy(); +} + +void PADSetAnalogMode(u32 mode) { + BOOL enabled; + u32 mask; + + ASSERTMSGLINE(1681, (mode < 8), "PADSetAnalogMode(): invalid mode"); + + enabled = OSDisableInterrupts(); + AnalogMode = mode << 8; + mask = EnabledBits; + + EnabledBits &= ~mask; + WaitingBits &= ~mask; + CheckingBits &= ~mask; + + SIDisablePolling(mask); + OSRestoreInterrupts(enabled); +} + +static void (*SamplingCallback)(); + +static BOOL OnShutdown(BOOL final, u32 event) { + BOOL sync; + static BOOL recalibrated = FALSE; + + if (SamplingCallback) + PADSetSamplingCallback(NULL); + + if (!final) { + sync = PADSync(); + if (!recalibrated && sync) { + recalibrated = PADRecalibrate(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT); + return FALSE; + } + return sync; + } else + recalibrated = FALSE; + + return TRUE; +} + +void __PADDisableXPatch(void) { + XPatchBits = 0; +} + +static void SamplingHandler(__OSInterrupt interrupt, OSContext* context) { + OSContext exceptionContext; + + if (SamplingCallback) { + OSClearContext(&exceptionContext); + OSSetCurrentContext(&exceptionContext); + SamplingCallback(); + OSClearContext(&exceptionContext); + OSSetCurrentContext(context); + } +} + +PADSamplingCallback PADSetSamplingCallback(PADSamplingCallback callback) { + PADSamplingCallback prev; + + prev = SamplingCallback; + SamplingCallback = callback; + if (callback) { + SIRegisterPollingHandler(SamplingHandler); + } else { + SIUnregisterPollingHandler(SamplingHandler); + } + + return prev; +} + +BOOL __PADDisableRecalibration(BOOL disable) { + BOOL enabled; + BOOL prev; + + enabled = OSDisableInterrupts(); + prev = (__gUnknown800030E3 & 0x40) ? TRUE : FALSE; + __gUnknown800030E3 &= ~0x40; + if (disable) { + __gUnknown800030E3 |= 0x40; + } + + OSRestoreInterrupts(enabled); + return prev; +} + +BOOL __PADDisableRumble(BOOL disable) { + BOOL enabled; + BOOL prev; + + enabled = OSDisableInterrupts(); + prev = (__gUnknown800030E3 & 0x20) ? TRUE : FALSE; + __gUnknown800030E3 &= ~0x20; + if (disable) { + __gUnknown800030E3 |= 0x20; + } + OSRestoreInterrupts(enabled); + return prev; +} + +BOOL PADIsBarrel(s32 chan) { + if (chan < 0 || chan >= 4) { + return FALSE; + } + + if (BarrelBits & (PAD_CHAN0_BIT >> chan)) { + return TRUE; + } + + return FALSE; +} diff --git a/src/revolution/pad/Padclamp.c b/src/revolution/pad/Padclamp.c new file mode 100644 index 0000000000..1348cf5d9e --- /dev/null +++ b/src/revolution/pad/Padclamp.c @@ -0,0 +1,153 @@ +#include +#include + +extern f32 sqrtf(f32); + +static const PADClampRegion ClampRegion = { + // Triggers + 30, + 180, + + // Left stick + 15, + 72, + 40, + + // Right stick + 15, + 59, + 31, + + // Stick radii + 56, + 44, +}; + +// prototypes +static void ClampStick(s8* px, s8* py, s8 max, s8 xy, s8 min); +static void ClampCircle(s8* px, s8* py, s8 radius, s8 min); +static void ClampTrigger(u8* trigger, u8 min, u8 max); + +static void ClampStick(s8* px, s8* py, s8 max, s8 xy, s8 min) { + int x = *px; + int y = *py; + int signX; + int signY; + int d; + + if (0 <= x) { + signX = 1; + } else { + signX = -1; + x = -x; + } + + if (0 <= y) { + signY = 1; + } else { + signY = -1; + y = -y; + } + + if (x <= min) { + x = 0; + } else { + x -= min; + } + if (y <= min) { + y = 0; + } else { + y -= min; + } + + if (x == 0 && y == 0) { + *px = *py = 0; + return; + } + + if (xy * y <= xy * x) { + d = xy * x + (max - xy) * y; + if (xy * max < d) { + x = (s8)(xy * max * x / d); + y = (s8)(xy * max * y / d); + } + } else { + d = xy * y + (max - xy) * x; + if (xy * max < d) { + x = (s8)(xy * max * x / d); + y = (s8)(xy * max * y / d); + } + } + + *px = (s8)(signX * x); + *py = (s8)(signY * y); +} + +static void ClampCircle(s8* px, s8* py, s8 radius, s8 min) { + int x = *px; + int y = *py; + int squared; + int length; + + if (-min < x && x < min) { + x = 0; + } else if (0 < x) { + x -= min; + } else { + x += min; + } + + if (-min < y && y < min) { + y = 0; + } else if (0 < y) { + y -= min; + } else { + y += min; + } + + squared = x * x + y * y; + if (radius * radius < squared) { + length = sqrtf(squared); + x = (x * radius) / length; + y = (y * radius) / length; + } + + *px = x; + *py = y; +} + +static void ClampTrigger(u8* trigger, u8 min, u8 max) { + if (*trigger <= min) { + *trigger = 0; + } else { + if (max < *trigger) { + *trigger = max; + } + *trigger -= min; + } +} + +void PADClamp(PADStatus * status) { + int i; + + for (i = 0; i < 4; i++, status++) { + if (status->err == PAD_ERR_NONE) { + ClampStick(&status->stickX, &status->stickY, ClampRegion.maxStick, ClampRegion.xyStick, ClampRegion.minStick); + ClampStick(&status->substickX, &status->substickY, ClampRegion.maxSubstick, ClampRegion.xySubstick, ClampRegion.minSubstick); + ClampTrigger(&status->triggerLeft, ClampRegion.minTrigger, ClampRegion.maxTrigger); + ClampTrigger(&status->triggerRight, ClampRegion.minTrigger, ClampRegion.maxTrigger); + } + } +} + +void PADClampCircle(PADStatus* status) { + int i; + for (i = 0; i < 4; ++i, status++) { + if (status->err == PAD_ERR_NONE) { + ClampCircle(&status->stickX, &status->stickY, ClampRegion.radStick, ClampRegion.minStick); + ClampCircle(&status->substickX, &status->substickY, ClampRegion.radSubstick, ClampRegion.minSubstick); + ClampTrigger(&status->triggerLeft, ClampRegion.minTrigger, ClampRegion.maxTrigger); + ClampTrigger(&status->triggerRight, ClampRegion.minTrigger, ClampRegion.maxTrigger); + } + } +} diff --git a/src/revolution/si/__si.h b/src/revolution/si/__si.h new file mode 100644 index 0000000000..52aebce923 --- /dev/null +++ b/src/revolution/si/__si.h @@ -0,0 +1,21 @@ +#ifndef _REVOLUTION_SI_INTERNAL_H_ +#define _REVOLUTION_SI_INTERNAL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void __SISteeringEnable(s32 chan); +s32 __SISteeringTransfer(s32 chan, u32 outputBytes, u32 inputBytes, void (*proc)(s32)); +void __SISteeringSyncCallback(s32 chan, s32); +s32 __SISteeringSync(s32 chan); +void __SISteeringDisable(s32 chan); +void __SITestSamplingRate(u32 tvmode); + +#ifdef __cplusplus +} +#endif + +#endif