diff --git a/sdk/Makefile b/sdk/Makefile index fef6f48..dcbc545 100644 --- a/sdk/Makefile +++ b/sdk/Makefile @@ -21,7 +21,7 @@ LINKFLAGS = -Wl,-z,max-page-size=0x1 CPPFLAGS = -mno-gpopt -nostartfiles -nostdlib -nodefaultlibs -ffreestanding -I$(IDIR) $(LINKFLAGS) -D$(SYSTEM)=1 -DFIRMWARE=$(FIRMWARE_NUM) -DEBOOT_VERSION=$(EBOOT_NUM) # Files -CPPFILES = $(wildcard $(SDIR)/*.cpp $(SDIR)/*/*.cpp $(SDIR)/*/*/*.cpp $(SDIR)/*/*/*/*.cpp) +CPPFILES = $(wildcard $(SDIR)/*.cpp $(SDIR)/*/*.cpp $(SDIR)/*/*/*.cpp $(SDIR)/*/*/*/*.cpp $(SDIR)/*/*/*/*/*.cpp $(SDIR)/*/*/*/*/*/*.cpp) OBJS = $(patsubst $(SDIR)/%.cpp, $(ODIR)/%.o, $(CPPFILES)) # Target diff --git a/sdk/include/arith64.hpp b/sdk/include/arith64.hpp new file mode 100644 index 0000000..390ec43 --- /dev/null +++ b/sdk/include/arith64.hpp @@ -0,0 +1,75 @@ +// GCC 32/64-bit integer arithmetic support for 32-bit systems that can't link +// to libgcc. + +// Function prototypes and descriptions are taken from +// https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html. + +// This file may be #include'd by another file, so we try not to pollute the +// namespace and we don't import any headers. + +// All functions must be resolvable by the linker and therefore can't be inline +// or static, even if they're #included into the file where they'll be used. + +// For best performance we try to avoid branching. This makes the code a little +// weird in places. + +// See https://github.com/glitchub/arith64 for more information. +// This software is released as-is into the public domain, as described at +// https://unlicense.org. Do whatever you like with it. + +#pragma once + +extern "C" +{ + #define arith64_u64 unsigned long long int + #define arith64_s64 signed long long int + #define arith64_u32 unsigned int + #define arith64_s32 int + + typedef union + { + arith64_u64 u64; + arith64_s64 s64; + struct + { + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_u32 hi; arith64_u32 lo; + #else + arith64_u32 lo; arith64_u32 hi; + #endif + } u32; + struct + { + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_s32 hi; arith64_s32 lo; + #else + arith64_s32 lo; arith64_s32 hi; + #endif + } s32; + } arith64_word; + + // extract hi and lo 32-bit words from 64-bit value + #define arith64_hi(n) (arith64_word){.u64=n}.u32.hi + #define arith64_lo(n) (arith64_word){.u64=n}.u32.lo + + // Negate a if b is negative, via invert and increment. + #define arith64_neg(a, b) (((a) ^ ((((arith64_s64)(b)) >= 0) - 1)) + (((arith64_s64)(b)) < 0)) + #define arith64_abs(a) arith64_neg(a, a) + + arith64_s64 __absvdi2(arith64_s64 a); + arith64_s64 __ashldi3(arith64_s64 a, int b); + arith64_s64 __ashrdi3(arith64_s64 a, int b); + int __clzsi2(arith64_u32 a); + int __clzdi2(arith64_u64 a); + int __ctzsi2(arith64_u32 a); + int __ctzdi2(arith64_u64 a); + arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c); + arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b); + int __ffsdi2(arith64_u64 a); + arith64_u64 __lshrdi3(arith64_u64 a, int b); + arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b); + int __popcountsi2(arith64_u32 a); + int __popcountdi2(arith64_u64 a); + arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b); + arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b); +} \ No newline at end of file diff --git a/sdk/include/common/list.hpp b/sdk/include/common/list.hpp new file mode 100644 index 0000000..1221e80 --- /dev/null +++ b/sdk/include/common/list.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +template +class List +{ +public: + List() + { + this->arraySize = 0; + this->arrayMaxSize = 2; + this->array = (T*)PS2::malloc(this->arrayMaxSize * sizeof(T)); + } + + List* add(T value) + { + if (this->array == nullptr) + return this; + + // Increase array size if we are out of room + if (this->arraySize >= this->arrayMaxSize) + { + uint32_t currentMemorySize = this->arrayMaxSize * sizeof(T); + + // Increase max size by double + this->arrayMaxSize *= 2; + + // Allocate a new space in memory + T* temp = (T*)PS2::malloc(this->arrayMaxSize * sizeof(T)); + + // Copy existing data to new location + PS2::memcpy(temp, this->array, currentMemorySize); + + // Deallocate existing location + PS2::free(this->array); + + this->array = temp; + } + + // Add value to list + this->array[this->arraySize] = value; + this->arraySize++; + + return this; + } + + T& get(uint32_t index) + { + // if (this->array == nullptr || index >= this->arraySize) + // throw 1; + return this->array[index]; + } + + T& operator[](int index) + { + return this->get(index); + } + + uint32_t size() + { + return this->arraySize; + } + + uint32_t maxSize() + { + return this->arrayMaxSize; + } + + void free() + { + PS2::free(this->array); + this->array = nullptr; + this->arraySize = 0; + this->arrayMaxSize = 0; + } +private: + T* array; + uint32_t arraySize; + uint32_t arrayMaxSize; +}; \ No newline at end of file diff --git a/sdk/include/mast1c0re.hpp b/sdk/include/mast1c0re.hpp index fc850d1..c0f46f3 100644 --- a/sdk/include/mast1c0re.hpp +++ b/sdk/include/mast1c0re.hpp @@ -2,19 +2,23 @@ #include #include +#include #include #include #include #include #include +#include #if (defined(PS4) && PS4) || (defined(PS5) && PS5) #include #include + #include #include #include #include #include + #include #include #include #include @@ -23,4 +27,8 @@ #include #include #include +#endif + +#if (defined(PS4) && PS4) + #include #endif \ No newline at end of file diff --git a/sdk/include/offsets/ps/eboot/1.01.hpp b/sdk/include/offsets/ps/eboot/1.01.hpp index c3cea1d..2867fe0 100644 --- a/sdk/include/offsets/ps/eboot/1.01.hpp +++ b/sdk/include/offsets/ps/eboot/1.01.hpp @@ -15,6 +15,19 @@ #define EBOOT_MOUNT_DISC_OPTIONS_STACK_OFFSET 0x0412AA0 // 0x7EEFFAAA0 - 0x7EEBE8000 #define EBOOT_MOUNT_DISC_GAME_CODE 0x0845a20 + // Configuration File + #define EBOOT_PROCESS_CONFIG_FILE_FUNC 0x043cd70 + #define EBOOT_LOAD_LUA_SCRIPTS_FUNC 0x0465a70 + #define EBOOT_LUA_CONFIG_DIRECTORY 0x0856ed0 + #define EBOOT_LUA_TROPHY_DIRECTORY 0x0856f00 + #define EBOOT_LUA_FEATURE_DIRECTORY 0x0856f30 + #define EBOOT_LUA_TOOLING_DIRECTORY 0x0856f60 + #define EBOOT_LUA_LOCAL_CONFIG_FILE 0x0856fc0 + + // Emu Command + #define EBOOT_EMU_COMMAND 0x0845a68 + #define EBOOT_EMU_COMMAND_COUNTER 0x0845a70 + // Function Stubs #define EBOOT_STRLEN_STUB 0x07635d0 #define EBOOT_EXIT_STUB 0x0763630 diff --git a/sdk/include/ps/memory.hpp b/sdk/include/ps/memory.hpp index 1638f5f..640c620 100644 --- a/sdk/include/ps/memory.hpp +++ b/sdk/include/ps/memory.hpp @@ -20,6 +20,12 @@ namespace PS PS::memcpy(address, VAR_TO_NATIVE(value), sizeof(T)); } + template + static void write(uint64_t address, uint32_t index, T value) + { + write(address + (sizeof(T) * index), value); + } + template static T read(uint64_t address) { @@ -28,12 +34,22 @@ namespace PS return value; } + template + static T read(uint64_t address, uint32_t index) + { + return read(address + (sizeof(T) * index)); + } + static void write(uint64_t address, void* buffer, size_t n); static void* read(uint64_t address, void* buffer, size_t n); static char* readString(uint64_t address); + + static void writeStdString(uint64_t address, const char* s); + static char* readStdString(uint64_t address); public: Memory(uint64_t address); + uint64_t getAddress(); PS::Memory* dereference(); PS::Memory* move(int64_t offset); @@ -43,13 +59,28 @@ namespace PS PS::Memory::write(this->address, value); } + template + void write(uint32_t index, T value) + { + PS::Memory::write(this->address + (sizeof(T) * index), value); + } + template T read() { return PS::Memory::read(this->address); } + template + T read(uint32_t index) + { + return PS::Memory::read(this->address + (sizeof(T) * index)); + } + char* readString(); + + void writeStdString(const char* s); + char* readStdString(); private: uint64_t address; }; diff --git a/sdk/include/ps/ps.hpp b/sdk/include/ps/ps.hpp index 1fb8648..3bb3daf 100644 --- a/sdk/include/ps/ps.hpp +++ b/sdk/include/ps/ps.hpp @@ -40,6 +40,31 @@ struct kevent void* udata; /* opaque user data identifier */ }; +#define uxMsg_Snapshot_Save 0x1f4 +#define uxMsg_Snapshot_SaveStamped 0x1f5 +#define uxMsg_Snapshot_Restore 0x1f6 +#define uxMsg_Snapshot_SaveCyclic 0x1f7 +#define uxMsg_ResetJIT_EE 0x1fe +#define uxMsg_ResetJIT_IOP 0x1ff +#define uxMsg_ResetJIT_VU 0x200 +#define uxMsg_ExitNicely 0x208 +#define uxMsg_StopExec 0x212 +#define uxMsg_StartExec 0x213 +#define uxMsg_ToggleExec 0x214 +#define uxMsg_StepExec 0x215 +#define uxMsg_EnableToolingMode 0x21c +#define uxMsg_GenCoreDump 0x221 +#define uxMsg_GsExternalCommand 0x226 +#define uxMsg_RestorePoint_Save 0x22b +#define uxMsg_RestorePoint_Restore 0x22c +#define uxMsg_StartVKLogging 0x230 +#define uxMsg_StopVKLogging 0x231 +#define uxMsg_SoftReset 0x23a +#define uxMsg_SwitchDisc 0x23b +#define uxMsg_SwitchDiscSwitch 0x23c +#define uxMsg_SwitchDiscClose 0x23d +#define uxMsg_ReloadLuaScripts 0x23e + namespace PS { #ifdef LIBKERNEL @@ -82,10 +107,41 @@ namespace PS uint32_t getpid(); int32_t PadSetLightBar(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); + void MountDisc(uint64_t mountDiscOptions); + char* GetMountedGameCode(); void SetMountOptionFilepath(const char* filepath); void MountDiscWithFilepath(const char* filepath); - char* GetMountedGameCode(); + + void ProcessConfigFile(const char* filepath); + // void ReloadLuaScriptsWithDirectory(const char* directory); + + // Emu Commands + void EmuSendCommand(uint32_t emuMsgId, uint64_t arg = 0); + void Snapshot_Save(uint64_t arg = 0); + void Snapshot_SaveStamped(uint64_t arg = 0); + void Snapshot_Restore(uint64_t arg = 0); + void Snapshot_SaveCyclic(uint64_t arg = 0); + void ResetJIT_EE(uint64_t arg = 0); + void ResetJIT_IOP(uint64_t arg = 0); + void ResetJIT_VU(uint64_t arg = 0); + void ExitNicely(uint64_t arg = 0); + void StopExec(uint64_t arg = 0); + void StartExec(uint64_t arg = 0); + void ToggleExec(uint64_t arg = 0); + void StepExec(uint64_t arg = 0); + void EnableToolingMode(uint64_t arg = 0); + void GenCoreDump(uint64_t arg = 0); + void GsExternalCommand(uint64_t arg = 0); + void RestorePoint_Save(uint64_t arg = 0); + void RestorePoint_Restore(uint64_t arg = 0); + void StartVKLogging(uint64_t arg = 0); + void StopVKLogging(uint64_t arg = 0); + void SoftReset(uint64_t arg = 0); + void SwitchDisc(uint64_t arg = 0); + void SwitchDiscSwitch(uint64_t arg = 0); + void SwitchDiscClose(uint64_t arg = 0); + void ReloadLuaScripts(uint64_t arg = 0); int32_t readAll(int32_t fd, void* buf, size_t len); size_t writeAll(int32_t fd, void* buf, size_t len); diff --git a/sdk/include/ps/sce/usb/filesystems/exfat/boot-sector.hpp b/sdk/include/ps/sce/usb/filesystems/exfat/boot-sector.hpp new file mode 100644 index 0000000..81d0205 --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/exfat/boot-sector.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +namespace exFAT +{ + #pragma pack(push, 1) + class BootSector + { + public: + inline uint32_t getBytesPerSector() + { + return 1 << this->bytesPerSectorShift; + } + + inline uint32_t getSectorsPerCluster() + { + return 1 << this->sectorsPerClusterShift; + } + + inline uint32_t getClusterSize() + { + return this->getBytesPerSector() * this->getSectorsPerCluster(); + } + + inline uint32_t getFatSize() + { + return this->getBytesPerSector() * this->sectorsPerFAT; + } + + inline uint32_t getFatBase() + { + return this->fatSectorOffset; + } + + inline uint32_t getDataBase() + { + return this->clusterHeapOffset; + } + public: + uint8_t jumpCode[3]; // 0x00 + char filesystemName[8]; // 0x03 + uint8_t zero[53]; // 0x0b: Zeros filling BPB of FAT filesystem + uint64_t partitionSectorOffset; // 0x40 + uint64_t volumeLength; // 0x48 + uint32_t fatSectorOffset; // 0x50 + uint32_t sectorsPerFAT; // 0x54 + uint32_t clusterHeapOffset; // 0x58 + uint32_t clusterCount; // 0x5c + uint32_t rootCluster; // 0x60 + uint32_t serialNumber; // 0x64 + uint16_t revision; // 0x68 + uint16_t volumeFlags; // 0x6a + uint8_t bytesPerSectorShift; // 0x6c + uint8_t sectorsPerClusterShift; // 0x6d + uint8_t numberOfFATs; // 0x6e + uint8_t driveSelect; // 0x6f + uint8_t percentInUse; // 0x70 + uint8_t reserved[7]; // 0x71 + uint8_t bootstrapCode[390]; // 0x78 + uint16_t signature; // 0x1fe + }; + #pragma pack(pop) +} \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/exfat/directory.hpp b/sdk/include/ps/sce/usb/filesystems/exfat/directory.hpp new file mode 100644 index 0000000..e7182cc --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/exfat/directory.hpp @@ -0,0 +1,19 @@ +#pragma once + +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include +#include + +namespace exFAT +{ + class Directory : public Record + { + public: + Directory(); + Directory(char* name, exFAT::FileDirectoryEntry entry, exFAT::StreamExtensionEntry stream, uint32_t bytesPerCluster); + }; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/exfat/entry.hpp b/sdk/include/ps/sce/usb/filesystems/exfat/entry.hpp new file mode 100644 index 0000000..8d9f865 --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/exfat/entry.hpp @@ -0,0 +1,158 @@ +#pragma once + +#include + +#define EXFAT_ENTRY_TYPE_CODE_ALLOCATION_BITMAP 0x81 +#define EXFAT_ENTRY_TYPE_CODE_UP_CASE_TABLE 0x82 +#define EXFAT_ENTRY_TYPE_CODE_VOLUME_LABEL 0x83 +#define EXFAT_ENTRY_TYPE_CODE_FILE_AND_DIRECTORY 0x85 +#define EXFAT_ENTRY_TYPE_CODE_STREAM_EXTENSION 0xc0 +#define EXFAT_ENTRY_TYPE_CODE_FILE_NAME 0xc1 +#define EXFAT_ENTRY_TYPE_CODE_WINDOWS_CE_ACCESS_CONTROL_LIST 0xc2 +#define EXFAT_ENTRY_TYPE_CODE_VOLUME_GUID 0xa0 +#define EXFAT_ENTRY_TYPE_CODE_TEXFAT_PADDING 0xa1 +#define EXFAT_ENTRY_TYPE_CODE_WINDOWS_CE_ACCESS_CONTROL_TABLE 0xa2 + +#define EXFAT_FILE_ATTRIBUTE_READ_ONLY (1 << 0) +#define EXFAT_FILE_ATTRIBUTE_HIDDEN (1 << 1) +#define EXFAT_FILE_ATTRIBUTE_SYSTEM (1 << 2) +#define EXFAT_FILE_ATTRIBUTE_DIRECTORY (1 << 4) +#define EXFAT_FILE_ATTRIBUTE_ARCHIVE (1 << 5) + +#define EXFAT_SECONDARY_ALLOCATION_POSSIBLE (1 << 0) +#define EXFAT_SECONDARY_NO_FAT_CHAIN (1 << 1) + +namespace exFAT +{ + #pragma pack(push, 1) + typedef struct + { + uint8_t type; // 0x00 + uint8_t bitmapFlags; // 0x01 + uint8_t unused[18]; // 0x02 + uint32_t firstCluster; // 0x14 + uint64_t length; // 0x18 + } AllocationBitmapEntry; // 0x20 + + typedef struct + { + uint8_t type; // 0x00 + uint8_t unused1[3]; // 0x01 + uint32_t tableChecksum; // 0x04 + uint8_t unused[12]; // 0x08 + uint32_t firstCluster; // 0x14 + uint64_t length; // 0x18 + } UpcaseTableEntry; // 0x20 + + typedef struct + { + uint8_t type; // 0x00 + uint8_t length; // 0x01 + uint8_t label[22]; // 0x02 + uint8_t unused[8]; // 0x18 + } VolumeLabelEntry; // 0x20 + + typedef struct + { + uint8_t type; // 0x00 + uint8_t secondaryCount; // 0x01 + uint16_t setChecksum; // 0x02 + uint16_t fileAttributes; // 0x04 + uint8_t unused1[2]; // 0x06 + uint32_t createdTimestamp; // 0x08 + uint32_t lastModifiedTimestamp; // 0x0c + uint32_t lastAccessedTimestamp; // 0x10 + uint8_t create10msIncrement; // 0x14 + uint8_t lastModified10msIncrement; // 0x15 + uint8_t createUtcOffset; // 0x16 + uint8_t lastModifiedUtcOffset; // 0x17 + uint8_t lastAccessedUtcOffset; // 0x18 + uint8_t unused2[7]; // 0x19 + } FileDirectoryEntry; // 0x20 + + typedef struct + { + uint8_t type; // 0x00 + uint8_t secondaryCount; // 0x01 + uint16_t setChecksum; // 0x02 + uint16_t generalPrimaryFlags; // 0x04 + uint8_t guid[16]; // 0x06 + uint8_t unused[10]; // 0x16 + } VolumeGUIDEntry; // 0x20 + + typedef struct + { + uint8_t type; // 0x00 + uint8_t generalSecondaryFlags; // 0x01 + uint8_t unused1; // 0x02 + uint8_t nameLength; // 0x03 + uint16_t nameHash; // 0x04 + uint8_t unused2[2]; // 0x06 + uint64_t validDataLength; // 0x08 + uint8_t unused3[4]; // 0x10 + uint32_t firstCluster; // 0x14 + uint64_t length; // 0x18 + } StreamExtensionEntry; // 0x20 + + typedef struct + { + uint8_t type; // 0x00 + uint8_t generalSecondaryFlags; // 0x01 + uint8_t filename[30]; // 0x02 + } FilenameEntry; // 0x20 + + class Entry + { + public: + inline uint32_t getCluster() + { + return this->firstCluster; + } + + inline uint32_t getLength() + { + return this->length; + } + + inline uint8_t getType() + { + return this->type; + } + + inline uint8_t getTypeCode() + { + return this->type & 0x1f; + } + + inline uint8_t getTypeImportance() + { + return (this->type >> 5) & 1; + } + + inline uint8_t getTypeCategory() + { + return (this->type >> 6) & 1; + } + + inline uint8_t getInUse() + { + return (this->type >> 7) & 1; + } + + inline bool isEndOfDirectory() + { + return this->type == 0x00; + } + + inline bool isUnused() + { + return this->type >= 0x01 && this->type <= 0x7f; + } + private: + uint8_t type; // 0x00 + uint8_t unused[19]; // 0x01 + uint32_t firstCluster; // 0x14 + uint64_t length; // 0x18 + }; // 0x20 + #pragma pack(pop) +}; \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/exfat/file.hpp b/sdk/include/ps/sce/usb/filesystems/exfat/file.hpp new file mode 100644 index 0000000..735de4a --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/exfat/file.hpp @@ -0,0 +1,20 @@ +#pragma once + +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include +#include + +namespace exFAT +{ + class File : public Record + { + public: + File(); + File(char* name, exFAT::FileDirectoryEntry entry, exFAT::StreamExtensionEntry stream, uint32_t bytesPerCluster); + bool hasExtension(const char* extension); + }; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/exfat/filesystem.hpp b/sdk/include/ps/sce/usb/filesystems/exfat/filesystem.hpp new file mode 100644 index 0000000..4ffa4b0 --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/exfat/filesystem.hpp @@ -0,0 +1,82 @@ +#pragma once + +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include +#include +#include +#include +#include +#include + +#define SECTOR_SIZE 512 +#define EXFAT_END_OF_CLUSTER_CHAIN 0xFFFFFFFF + +// See: https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/FileIO/exfat-specification.md + +namespace exFAT +{ + class Filesystem + { + public: + Filesystem(); + Filesystem(PS::MassStore* massStore, uint32_t baseAddress, exFAT::BootSector bootSector); + bool isMounted(); + bool mount(); + uint32_t getClusterSize(); + bool root(List* directories, List* files); + bool directory(const char* filepath, List* directories, List* files = nullptr); + bool directory(exFAT::Directory parent, List* directories, List* files = nullptr); + List directories(const char* filepath); + List directories(exFAT::Directory parent); + List files(const char* filepath); + List files(exFAT::Directory parent); + bool exists(const char* filepath); + bool resetRead(exFAT::File file); + bool readNextCluster(exFAT::File file, uint8_t* buffer); + private: + bool getDirectoryFromFilepath(const char* filepath, exFAT::Directory* directory); + bool directory(uint32_t cluster, List* directories, List* files = nullptr); + bool readCluster(uint32_t blockOffset, uint8_t* buffer); + bool cacheSector(uint32_t blockOffset); + uint32_t getNextCluster(uint32_t cluster); + void sortDirectories(List* directories); + void sortFiles(List* files); + char* strcatwn(char* destination, uint8_t* source, uint32_t slen); + + inline uint32_t getSectorIndexForCluster(uint32_t index) + { + if (index < 2) + index = 2; + return this->dataBase + ((index - 2) * this->bootSector.getSectorsPerCluster()); + } + + inline bool readSector(uint32_t blockOffset, uint8_t* buffer) + { + return this->readSectors(blockOffset, buffer, 1); + } + + inline bool readSectors(uint32_t blockOffset, uint8_t* buffer, uint32_t count) + { + return this->massStore->readBlock(this->baseAddress + blockOffset, count, SECTOR_SIZE, buffer) == SCE_OK; + } + private: + bool mounted; + PS::MassStore* massStore; + uint32_t baseAddress; + exFAT::BootSector bootSector; + uint32_t clusterSize; + uint32_t fatSize; + uint32_t fatBase; + uint32_t dataBase; + + uint8_t cache[SECTOR_SIZE]; + uint32_t cacheIndex; + + uint32_t readStartClusterIndex; + uint32_t readClusterIndex; + }; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/exfat/record.hpp b/sdk/include/ps/sce/usb/filesystems/exfat/record.hpp new file mode 100644 index 0000000..d5013d7 --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/exfat/record.hpp @@ -0,0 +1,48 @@ +#pragma once + +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include + +namespace exFAT +{ + class Record + { + public: + Record(); + Record(char* name, exFAT::FileDirectoryEntry entry, exFAT::StreamExtensionEntry stream, uint32_t bytesPerCluster); + + inline char* getName() + { + return this->name; + } + + inline uint32_t getCluster() + { + return this->stream.firstCluster; + } + + inline uint32_t getClusterCount() + { + return this->clusterCount; + } + + inline uint64_t getSize() + { + return this->stream.length; + } + + inline bool isContinuous() + { + return (this->stream.generalSecondaryFlags & EXFAT_SECONDARY_NO_FAT_CHAIN) == EXFAT_SECONDARY_NO_FAT_CHAIN; + } + protected: + char name[256]; + uint32_t clusterCount; + exFAT::FileDirectoryEntry entry; + exFAT::StreamExtensionEntry stream; + }; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/mbr/master-boot-record-table.hpp b/sdk/include/ps/sce/usb/filesystems/mbr/master-boot-record-table.hpp new file mode 100644 index 0000000..d75ba8d --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/mbr/master-boot-record-table.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#define MBR_SIGNATURE 0xaa55 +#define MBR_MAX_PARTITIONS 4 + +#pragma pack(push, 1) +class MasterBootRecordTable +{ +public: + inline bool isValid() + { + return this->signature == MBR_SIGNATURE; + } + + inline uint32_t getMaxParitionCount() + { + return MBR_MAX_PARTITIONS; + } + + inline uint16_t getSignature() + { + return this->signature; + } + + inline MasterBootRecord getParition(uint32_t index) + { + return this->partitions[index]; + } +public: + uint8_t bootstrapCode[446]; + MasterBootRecord partitions[MBR_MAX_PARTITIONS]; + uint16_t signature; +}; +#pragma pack(pop) \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/filesystems/mbr/master-boot-record.hpp b/sdk/include/ps/sce/usb/filesystems/mbr/master-boot-record.hpp new file mode 100644 index 0000000..d124177 --- /dev/null +++ b/sdk/include/ps/sce/usb/filesystems/mbr/master-boot-record.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#define MBR_PARTITION_TYPE_EMPTY 0x00 + +#pragma pack(push, 1) +class MasterBootRecord +{ +public: + inline bool isEmpty() + { + return this->partitionType == MBR_PARTITION_TYPE_EMPTY || this->logicalBlockAddress == 0; + } +public: + uint8_t bootIndicator; + uint8_t startHead; + uint8_t startSector; + uint8_t startTrack; + uint8_t partitionType; + uint8_t endHead; + uint8_t endSector; + uint8_t endTrack; + uint32_t logicalBlockAddress; + uint32_t sectorCount; +}; +#pragma pack(pop) \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/mass-store.hpp b/sdk/include/ps/sce/usb/mass-store.hpp new file mode 100644 index 0000000..be9da9d --- /dev/null +++ b/sdk/include/ps/sce/usb/mass-store.hpp @@ -0,0 +1,48 @@ +#pragma once + +#if (defined(PS4) && PS4) +#include +#include +#ifdef LIBKERNEL +#include +#include + +#define MASS_STORE_CBW_SIGNATURE 0x43425355UL // Command Block Wrapper signature byte, for verification of valid CBW blocks +#define MASS_STORE_CSW_SIGNATURE 0x53425355UL // Command Static Wrapper signature byte, for verification of valid CSW blocks + +#define MASS_STORE_COMMAND_DIRECTION_DATA_OUT (0 << 7) // Data direction mask for the Flags field of a CBW, indicating Host-to-Device transfer direction +#define MASS_STORE_COMMAND_DIRECTION_DATA_IN (1 << 7) // Data direction mask for the Flags field of a CBW, indicating Device-to-Host transfer direction +#define MASS_STORE_COMMAND_DATA_TIMEOUT_MS 10000 // Timeout period between the issuing of a CBW to a device, and the reception of the first packet +#define MASS_STORE_DATA_IN_PIPE 1 // Pipe number of the Mass Storage data IN pipe +#define MASS_STORE_DATA_OUT_PIPE 2 // Pipe number of the Mass Storage data OUT pipe + +#define MASS_STORE_BULK_ONLY_DEFAULT_ENDPOINT_IN 0x81 +#define MASS_STORE_BULK_ONLY_DEFAULT_ENDPOINT_OUT 0x02 + +namespace PS +{ + class MassStore + { + public: + MassStore(); + MassStore(PS::Sce::Usbd* usbd, uint8_t endpointIn = 0, uint8_t endpointOut = 0); + + int32_t inquiry(SCSI::Inquiry* inquiry); + int32_t readCapacity(SCSI::Capacity* capacity); + int32_t readBlock(uint32_t blockAddress, uint16_t blocks, uint16_t blockSize, uint8_t* buffer); + private: + int32_t command(SCSI::CommandBlock* command, uint8_t* buffer); + int32_t sendReceiveData(SCSI::CommandBlock* command, uint8_t* buffer); + int32_t getReturnedStatus(SCSI::CommandStatus* status); + uint8_t getEndpointIn(); + uint8_t getEndpointOut(); + private: + static uint32_t tag; + private: + PS::Sce::Usbd* usbd; + uint8_t endpointIn; + uint8_t endpointOut; + }; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/scsi.hpp b/sdk/include/ps/sce/usb/scsi.hpp new file mode 100644 index 0000000..96410cd --- /dev/null +++ b/sdk/include/ps/sce/usb/scsi.hpp @@ -0,0 +1,105 @@ +#include + +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_READ_CAPACITY_10 0x25 +#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D +#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E +#define SCSI_CMD_WRITE_10 0x2A +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_WRITE_6 0x0A +#define SCSI_CMD_READ_6 0x08 +#define SCSI_CMD_VERIFY_10 0x2F +#define SCSI_CMD_MODE_SENSE_6 0x1A +#define SCSI_CMD_MODE_SENSE_10 0x5A + +#define SCSI_SENSE_KEY_GOOD 0x00 +#define SCSI_SENSE_KEY_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_KEY_NOT_READY 0x02 +#define SCSI_SENSE_KEY_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_KEY_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_KEY_DATA_PROTECT 0x07 +#define SCSI_SENSE_KEY_BLANK_CHECK 0x08 +#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09 +#define SCSI_SENSE_KEY_COPY_ABORTED 0x0A +#define SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D +#define SCSI_SENSE_KEY_MISCOMPARE 0x0E + +#define SCSI_ASENSE_NO_ADDITIONAL_INFORMATION 0x00 +#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04 +#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24 +#define SCSI_ASENSE_WRITE_PROTECTED 0x27 +#define SCSI_ASENSE_FORMAT_ERROR 0x31 +#define SCSI_ASENSE_INVALID_COMMAND 0x20 +#define SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21 +#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A + +#define SCSI_ASENSEQ_NO_QUALIFIER 0x00 +#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01 +#define SCSI_ASENSEQ_INITIALIZING_COMMAND_REQUIRED 0x02 +#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07 + +namespace SCSI +{ + typedef struct + { + uint32_t signature; // Command block signature, always equal to CBW_SIGNATURE + uint32_t tag; // Current CBW tag, to positively associate a CBW with a CSW (filled automatically) + uint32_t dataTransferLength; // Length of data to transfer, following the CBW + uint8_t flags; // Block flags, equal to one of the COMMAND_DIRECTION_DATA_* macros + uint8_t lun; // Logical Unit Number the CBW is addressed to in the device + uint8_t scsiCommandLength; // Length of the SCSI command in the CBW + uint8_t scsiCommandData[16]; // SCSI command to issue to the device + } __attribute__ ((packed)) CommandBlock; + + typedef struct + { + uint32_t signature; // Command status signature, always equal to CSW_SIGNATURE + uint32_t tag; // Current CBW tag, to positively associate a CBW with a CSW + uint32_t dataTransferResidue; // Length of data not transferred + uint8_t status; // Command status + } __attribute__ ((packed)) CommandStatus; + + typedef struct + { + uint32_t blocks; // Number of blocks in the addressed LUN of the device + uint32_t blockSize; // Number of bytes in each block in the addressed LUN + } __attribute__ ((packed)) Capacity; + + typedef struct + { + uint8_t deviceType : 5; + uint8_t peripheralQualifier : 3; + + uint8_t _reserved1 : 7; + uint8_t removable : 1; + + uint8_t version; + + uint8_t responseDataFormat : 4; + uint8_t _reserved2 : 1; + uint8_t normACA : 1; + uint8_t trmTsk : 1; + uint8_t aerc : 1; + + uint8_t additionalLength; + uint8_t _reserved3[2]; + + uint8_t softReset : 1; + uint8_t cmdQue : 1; + uint8_t _reserved4 : 1; + uint8_t linked : 1; + uint8_t sync : 1; + uint8_t wideBus16Bit : 1; + uint8_t wideBus32Bit : 1; + uint8_t relAddr : 1; + + uint8_t vendorID[8]; + uint8_t productID[16]; + uint8_t revisionID[4]; + } __attribute__ ((packed)) Inquiry; +} \ No newline at end of file diff --git a/sdk/include/ps/sce/usb/usb/usb.hpp b/sdk/include/ps/sce/usb/usb/usb.hpp new file mode 100644 index 0000000..d8be041 --- /dev/null +++ b/sdk/include/ps/sce/usb/usb/usb.hpp @@ -0,0 +1,65 @@ +#pragma once + +#if (defined(PS4) && PS4) +#include +#include +#ifdef LIBKERNEL +#include +#include +#include +#include +#include +#include + +class Usb +{ +public: + static List list(); +public: + Usb(); + Usb(uint64_t device); + Usb(uint16_t vid, uint16_t pid); + inline bool isOpen(); + bool open(); + bool mount(); + void unmount(); + inline bool isMounted(); + void close(); + uint32_t getClusterSize(); + + bool root(List* directories, List* files); + bool directory(const char* filepath, List* directories, List* files); + bool directory(exFAT::Directory parent, List* directories, List* files); + List directories(const char* filepath); + List directories(exFAT::Directory parent); + List files(const char* filepath); + List files(exFAT::Directory parent); + bool exists(const char* filepath); + bool resetRead(exFAT::File file); + bool readNextCluster(exFAT::File file, uint8_t* buffer); +private: + bool updateDescriptors(); + bool readSector(uint32_t blockAddress, uint8_t* buffer); + bool readSectors(uint32_t blockAddress, uint8_t* buffer, uint32_t count = 1); +private: + static uint64_t devices; +private: + uint64_t device; + uint16_t vid; + uint16_t pid; + uint8_t endpointIn; + uint8_t endpointOut; + PS::Sce::Usbd usbd; + PS::MassStore massStore; + MasterBootRecord partition; + exFAT::Filesystem filesystem; + bool mounted; + + char vendor[9]; + char product[17]; + + uint32_t sectorCount; + uint32_t sectorSize; +}; +#endif +#endif \ No newline at end of file diff --git a/sdk/include/ps/sce/usbd.hpp b/sdk/include/ps/sce/usb/usbd.hpp similarity index 62% rename from sdk/include/ps/sce/usbd.hpp rename to sdk/include/ps/sce/usb/usbd.hpp index 460c63a..f61d7a0 100644 --- a/sdk/include/ps/sce/usbd.hpp +++ b/sdk/include/ps/sce/usb/usbd.hpp @@ -1,10 +1,12 @@ #pragma once -#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#if (defined(PS4) && PS4) #include #include #ifdef LIBKERNEL +// See: https://libusb.sourceforge.io/api-1.0/libusb_api.html + #define SCE_USBD_ERROR_ACCESS 0x80240003 // CE-30793-2 #define SCE_USBD_ERROR_BUSY 0x80240006 // CE-30796-5 #define SCE_USBD_ERROR_FATAL 0x802400ff // CE-30803-4 @@ -24,6 +26,85 @@ #define SCE_USBD_IN_TIMEOUT 1000 #define SCE_USBD_CONTROL_TIMEOUT 1000 +#define LIBUSB_CLASS_PER_INTERFACE 0 +#define LIBUSB_CLASS_AUDIO 1 +#define LIBUSB_CLASS_COMM 2 +#define LIBUSB_CLASS_HID 3 +#define LIBUSB_CLASS_PHYSICAL 5 +#define LIBUSB_CLASS_PRINTER 7 +#define LIBUSB_CLASS_PTP 6 +#define LIBUSB_CLASS_IMAGE 6 +#define LIBUSB_CLASS_MASS_STORAGE 8 +#define LIBUSB_CLASS_HUB 9 +#define LIBUSB_CLASS_DATA 10 +#define LIBUSB_CLASS_SMART_CARD 0x0b +#define LIBUSB_CLASS_CONTENT_SECURITY 0x0d +#define LIBUSB_CLASS_VIDEO 0x0e +#define LIBUSB_CLASS_PERSONAL_HEALTHCARE 0x0f +#define LIBUSB_CLASS_DIAGNOSTIC_DEVICE 0xdc +#define LIBUSB_CLASS_WIRELESS 0xe0 +#define LIBUSB_CLASS_APPLICATION 0xfe +#define LIBUSB_CLASS_VENDOR_SPEC 0xff + +#define LIBUSB_DT_DEVICE 0x01 +#define LIBUSB_DT_CONFIG 0x02 +#define LIBUSB_DT_STRING 0x03 +#define LIBUSB_DT_INTERFACE 0x04 +#define LIBUSB_DT_ENDPOINT 0x05 +#define LIBUSB_DT_BOS 0x0f +#define LIBUSB_DT_DEVICE_CAPABILITY 0x10 +#define LIBUSB_DT_HID 0x21 +#define LIBUSB_DT_REPORT 0x22 +#define LIBUSB_DT_PHYSICAL 0x23 +#define LIBUSB_DT_HUB 0x29 +#define LIBUSB_DT_SUPERSPEED_HUB 0x2a +#define LIBUSB_DT_SS_ENDPOINT_COMPANION 0x30 + +#define LIBUSB_ENDPOINT_IN 0x80 +#define LIBUSB_ENDPOINT_OUT 0x00 + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 + +#define LIBUSB_TRANSFER_TYPE_CONTROL 0 +#define LIBUSB_TRANSFER_TYPE_ISOCHRONOUS 1 +#define LIBUSB_TRANSFER_TYPE_BULK 2 +#define LIBUSB_TRANSFER_TYPE_INTERRUPT 3 +#define LIBUSB_TRANSFER_TYPE_BULK_STREAM 4 + +#define LIBUSB_REQUEST_GET_STATUS 0x00 +#define LIBUSB_REQUEST_CLEAR_FEATURE 0x01 +#define LIBUSB_REQUEST_SET_FEATURE 0x03 +#define LIBUSB_REQUEST_SET_ADDRESS 0x05 +#define LIBUSB_REQUEST_GET_DESCRIPTOR 0x06 +#define LIBUSB_REQUEST_SET_DESCRIPTOR 0x07 +#define LIBUSB_REQUEST_GET_CONFIGURATION 0x08 +#define LIBUSB_REQUEST_SET_CONFIGURATION 0x09 +#define LIBUSB_REQUEST_GET_INTERFACE 0x0A +#define LIBUSB_REQUEST_SET_INTERFACE 0x0B +#define LIBUSB_REQUEST_SYNCH_FRAME 0x0C +#define LIBUSB_REQUEST_SET_SEL 0x30 +#define LIBUSB_SET_ISOCH_DELAY 0x31 + +#define LIBUSB_RECIPIENT_DEVICE 0x00 +#define LIBUSB_RECIPIENT_INTERFACE 0x01 +#define LIBUSB_RECIPIENT_ENDPOINT 0x02 +#define LIBUSB_RECIPIENT_OTHER 0x03 + +#define LIBUSB_SUCCESS 0 +#define LIBUSB_ERROR_IO -1 +#define LIBUSB_ERROR_INVALID_PARAM -2 +#define LIBUSB_ERROR_ACCESS -3 +#define LIBUSB_ERROR_NO_DEVICE -4 +#define LIBUSB_ERROR_NOT_FOUND -5 +#define LIBUSB_ERROR_BUSY -6 +#define LIBUSB_ERROR_TIMEOUT -7 +#define LIBUSB_ERROR_OVERFLOW -8 +#define LIBUSB_ERROR_PIPE -9 +#define LIBUSB_ERROR_INTERRUPTED -10 +#define LIBUSB_ERROR_NO_MEM -11 +#define LIBUSB_ERROR_NOT_SUPPORTED -12 +#define LIBUSB_ERROR_OTHER -99 + namespace PS { namespace Sce @@ -59,7 +140,7 @@ namespace PS uint8_t interval; uint8_t refresh; uint8_t synchAddress; - unsigned char *extra; + uint64_t /* unsigned char* */ extra; int extraLength; } EndpointDescriptor; @@ -74,14 +155,14 @@ namespace PS uint8_t interfaceSubClass; uint8_t interfaceProtocol; uint8_t interface; - PS::Sce::Usbd::EndpointDescriptor *endpoint; + uint64_t /* PS::Sce::Usbd::EndpointDescriptor* */ endpoint; unsigned char *extra; int extraLength; } InterfaceDescriptor; typedef struct { - PS::Sce::Usbd::InterfaceDescriptor *altSetting; + uint64_t /* PS::Sce::Usbd::InterfaceDescriptor* */ altSetting; int numAltSetting; } Interface; @@ -95,8 +176,8 @@ namespace PS uint8_t configuration; uint8_t attributes; uint8_t maxPower; - PS::Sce::Usbd::Interface *interface; - unsigned char *extra; + uint64_t /* PS::Sce::Usbd::Interface* */ interface; + uint64_t /* unsigned char* */ extra; int extraLength; } ConfigDescriptor; public: @@ -111,7 +192,7 @@ namespace PS static int32_t GetActiveConfigDescriptor(uint64_t device, uint64_t* /* Sce::Usbd::ConfigDescriptor** */ config); static int32_t GetConfigDescriptor(uint64_t device, uint8_t configIndex, uint64_t* /* Sce::Usbd::ConfigDescriptor** */ config); static int32_t GetConfigDescriptorByValue(uint64_t device, uint8_t bConfigurationValue, uint64_t* /* Sce::Usbd::ConfigDescriptor** */ config); - static void FreeConfigDescriptor(Sce::Usbd::ConfigDescriptor* config); + static void FreeConfigDescriptor(uint64_t /* Sce::Usbd::ConfigDescriptor* */ config); private: static bool isInitialized; static int libUsbd; @@ -136,8 +217,8 @@ namespace PS static uint64_t pFreeConfigDescriptor; public: Usbd(); - Usbd(uint64_t device); Usbd(uint16_t vendorId, uint16_t productId); + bool Open(); void Close(); int32_t SetInterfaceAltSetting(int interfaceNumber, int alternateSetting); int32_t ClearHalt(uint8_t endpoint); @@ -152,6 +233,8 @@ namespace PS int32_t Receive(uint8_t endpoint, uint8_t* data, int32_t length, int32_t* received); public: uint64_t handle; + uint16_t vendorId; + uint16_t productId; }; } } diff --git a/sdk/src/arith64.cpp b/sdk/src/arith64.cpp new file mode 100644 index 0000000..d0a2e98 --- /dev/null +++ b/sdk/src/arith64.cpp @@ -0,0 +1,239 @@ +// GCC 32/64-bit integer arithmetic support for 32-bit systems that can't link +// to libgcc. + +// Function prototypes and descriptions are taken from +// https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html. + +// This file may be #include'd by another file, so we try not to pollute the +// namespace and we don't import any headers. + +// All functions must be resolvable by the linker and therefore can't be inline +// or static, even if they're #included into the file where they'll be used. + +// For best performance we try to avoid branching. This makes the code a little +// weird in places. + +// See https://github.com/glitchub/arith64 for more information. +// This software is released as-is into the public domain, as described at +// https://unlicense.org. Do whatever you like with it. + +#include + +extern "C" +{ + // Return the absolute value of a. + // Note LLINT_MIN cannot be negated. + arith64_s64 __absvdi2(arith64_s64 a) + { + return arith64_abs(a); + } + + // Return the result of shifting a left by b bits. + arith64_s64 __ashldi3(arith64_s64 a, int b) + { + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.hi = w.u32.lo << (b - 32); + w.u32.lo = 0; + } else if (b) + { + w.u32.hi = (w.u32.lo >> (32 - b)) | (w.u32.hi << b); + w.u32.lo <<= b; + } + return w.s64; + } + + // Return the result of arithmetically shifting a right by b bits. + arith64_s64 __ashrdi3(arith64_s64 a, int b) + { + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.s32.lo = w.s32.hi >> (b - 32); + w.s32.hi >>= 31; // 0xFFFFFFFF or 0 + } else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.s32.hi >>= b; + } + return w.s64; + } + + // These functions return the number of leading 0-bits in a, starting at the + // most significant bit position. If a is zero, the result is undefined. + int __clzsi2(arith64_u32 a) + { + int b, n = 0; + b = !(a & 0xffff0000) << 4; n += b; a <<= b; + b = !(a & 0xff000000) << 3; n += b; a <<= b; + b = !(a & 0xf0000000) << 2; n += b; a <<= b; + b = !(a & 0xc0000000) << 1; n += b; a <<= b; + return n + !(a & 0x80000000); + } + + int __clzdi2(arith64_u64 a) + { + int b, n = 0; + b = !(a & 0xffffffff00000000ULL) << 5; n += b; a <<= b; + b = !(a & 0xffff000000000000ULL) << 4; n += b; a <<= b; + b = !(a & 0xff00000000000000ULL) << 3; n += b; a <<= b; + b = !(a & 0xf000000000000000ULL) << 2; n += b; a <<= b; + b = !(a & 0xc000000000000000ULL) << 1; n += b; a <<= b; + return n + !(a & 0x8000000000000000ULL); + } + + // These functions return the number of trailing 0-bits in a, starting at the + // least significant bit position. If a is zero, the result is undefined. + int __ctzsi2(arith64_u32 a) + { + int b, n = 0; + b = !(a & 0x0000ffff) << 4; n += b; a >>= b; + b = !(a & 0x000000ff) << 3; n += b; a >>= b; + b = !(a & 0x0000000f) << 2; n += b; a >>= b; + b = !(a & 0x00000003) << 1; n += b; a >>= b; + return n + !(a & 0x00000001); + } + + int __ctzdi2(arith64_u64 a) + { + int b, n = 0; + b = !(a & 0x00000000ffffffffULL) << 5; n += b; a >>= b; + b = !(a & 0x000000000000ffffULL) << 4; n += b; a >>= b; + b = !(a & 0x00000000000000ffULL) << 3; n += b; a >>= b; + b = !(a & 0x000000000000000fULL) << 2; n += b; a >>= b; + b = !(a & 0x0000000000000003ULL) << 1; n += b; a >>= b; + return n + !(a & 0x0000000000000001ULL); + } + + // Calculate both the quotient and remainder of the unsigned division of a and + // b. The return value is the quotient, and the remainder is placed in variable + // pointed to by c (if it's not NULL). + arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c) + { + if (b > a) // divisor > numerator? + { + if (c) *c = a; // remainder = numerator + return 0; // quotient = 0 + } + if (!arith64_hi(b)) // divisor is 32-bit + { + if (b == 0) // divide by 0 + { + volatile char x = 0; x = 1 / x; // force an exception + } + if (b == 1) // divide by 1 + { + if (c) *c = 0; // remainder = 0 + return a; // quotient = numerator + } + if (!arith64_hi(a)) // numerator is also 32-bit + { + if (c) // use generic 32-bit operators + *c = arith64_lo(a) % arith64_lo(b); + return arith64_lo(a) / arith64_lo(b); + } + } + + // let's do long division + char bits = __clzdi2(b) - __clzdi2(a) + 1; // number of bits to iterate (a and b are non-zero) + arith64_u64 rem = a >> bits; // init remainder + a <<= 64 - bits; // shift numerator to the high bit + arith64_u64 wrap = 0; // start with wrap = 0 + while (bits-- > 0) // for each bit + { + rem = (rem << 1) | (a >> 63); // shift numerator MSB to remainder LSB + a = (a << 1) | (wrap & 1); // shift out the numerator, shift in wrap + wrap = ((arith64_s64)(b - rem - 1) >> 63); // wrap = (b > rem) ? 0 : 0xffffffffffffffff (via sign extension) + rem -= b & wrap; // if (wrap) rem -= b + } + if (c) *c = rem; // maybe set remainder + return (a << 1) | (wrap & 1); // return the quotient + } + + // Return the quotient of the signed division of a and b. + arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b) + { + arith64_u64 q = __divmoddi4(arith64_abs(a), arith64_abs(b), (unsigned long long *)0); + return arith64_neg(q, a^b); // negate q if a and b signs are different + } + + // Return the index of the least significant 1-bit in a, or the value zero if a + // is zero. The least significant bit is index one. + int __ffsdi2(arith64_u64 a) + { + return a ? __ctzdi2(a) + 1 : 0; + } + + // Return the result of logically shifting a right by b bits. + arith64_u64 __lshrdi3(arith64_u64 a, int b) + { + arith64_word w = {.u64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.lo = w.u32.hi >> (b - 32); + w.u32.hi = 0; + } else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.u32.hi >>= b; + } + return w.u64; + } + + // Return the remainder of the signed division of a and b. + arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b) + { + arith64_u64 r; + __divmoddi4(arith64_abs(a), arith64_abs(b), &r); + return arith64_neg(r, a); // negate remainder if numerator is negative + } + + // Return the number of bits set in a. + int __popcountsi2(arith64_u32 a) + { + // collect sums into two low bytes + a = a - ((a >> 1) & 0x55555555); + a = ((a >> 2) & 0x33333333) + (a & 0x33333333); + a = (a + (a >> 4)) & 0x0F0F0F0F; + a = (a + (a >> 16)); + // add the bytes, return bottom 6 bits + return (a + (a >> 8)) & 63; + } + + // Return the number of bits set in a. + int __popcountdi2(arith64_u64 a) + { + // collect sums into two low bytes + a = a - ((a >> 1) & 0x5555555555555555ULL); + a = ((a >> 2) & 0x3333333333333333ULL) + (a & 0x3333333333333333ULL); + a = (a + (a >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + a = (a + (a >> 32)); + a = (a + (a >> 16)); + // add the bytes, return bottom 7 bits + return (a + (a >> 8)) & 127; + } + + // Return the quotient of the unsigned division of a and b. + arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b) + { + return __divmoddi4(a, b, (unsigned long long *)0); + } + + // Return the remainder of the unsigned division of a and b. + arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b) + { + arith64_u64 r; + __divmoddi4(a, b, &r); + return r; + } +} \ No newline at end of file diff --git a/sdk/src/ps/memory.cpp b/sdk/src/ps/memory.cpp index 0af23fe..123ea27 100644 --- a/sdk/src/ps/memory.cpp +++ b/sdk/src/ps/memory.cpp @@ -73,11 +73,43 @@ char* PS::Memory::readString(uint64_t address) return buffer; } +void PS::Memory::writeStdString(uint64_t address, const char* s) +{ + PS::Memory::write(address + 0x08, PVAR_TO_NATIVE(s)); // buffer + PS::Memory::write(address + 0x18, PS2::strlen(s)); // length + PS::Memory::write(address + 0x20, 0x1f); // cap +} + +char* PS::Memory::readStdString(uint64_t address) +{ + uint64_t length = PS::Memory::read(address + 0x18); + uint64_t cap = PS::Memory::read(address + 0x20); + + // Allocate PS2 memory for string + char* buffer = (char*)PS2::malloc(length); + + // Buffer is a pointer + if (cap > 0xf) + { + uint64_t s = PS::Memory::read(address + 0x08); + return (char*)PS::Memory::read(s, buffer, length); + } + + // Buffer itself is string data + return (char*)PS::Memory::read(address + 0x08, buffer, length); +} + + PS::Memory::Memory(uint64_t address) { this->address = address; } +uint64_t PS::Memory::getAddress() +{ + return this->address; +} + PS::Memory* PS::Memory::dereference() { this->address = PS::Memory::read(this->address); @@ -94,4 +126,16 @@ char* PS::Memory::readString() { return PS::Memory::readString(this->address); } + +void PS::Memory::writeStdString(const char* s) +{ + PS::Memory::writeStdString(this->address, s); +} + +char* PS::Memory::readStdString() +{ + + return PS::Memory::readStdString(this->address); +} + #endif \ No newline at end of file diff --git a/sdk/src/ps/ps.cpp b/sdk/src/ps/ps.cpp index 15fe592..eba2eb5 100644 --- a/sdk/src/ps/ps.cpp +++ b/sdk/src/ps/ps.cpp @@ -176,11 +176,15 @@ void PS::MountDisc(uint64_t mountDiscOptions) PS::Breakout::call(EBOOT(EBOOT_MOUNT_DISC_FUNC), mountDiscOptions); } +char* PS::GetMountedGameCode() +{ + return PS::Memory(EBOOT(EBOOT_MOUNT_DISC_GAME_CODE)).move(0x08)->readString(); +} + void PS::SetMountOptionFilepath(const char* filepath) { uint64_t mountDiscOptions = STACK(EBOOT_MOUNT_DISC_OPTIONS_STACK_OFFSET); - PS::Memory(mountDiscOptions).move(0x10)->write(PVAR_TO_NATIVE(filepath)); - PS::Memory(mountDiscOptions).move(0x20)->write(PS2::strlen(filepath)); + PS::Memory(mountDiscOptions).move(0x08)->writeStdString(filepath); } void PS::MountDiscWithFilepath(const char* filepath) @@ -189,10 +193,165 @@ void PS::MountDiscWithFilepath(const char* filepath) PS::MountDisc(STACK(EBOOT_MOUNT_DISC_OPTIONS_STACK_OFFSET)); } -char* PS::GetMountedGameCode() +void PS::ProcessConfigFile(const char* filepath) { - // Get name from mounted file - return PS::Memory(EBOOT(EBOOT_MOUNT_DISC_GAME_CODE)).move(0x08)->readString(); + PS::Breakout::call(EBOOT(EBOOT_PROCESS_CONFIG_FILE_FUNC), PVAR_TO_NATIVE(filepath)); +} + +// void PS::ReloadLuaScriptsWithDirectory(const char* directory) +// { +// // LuaConfigDirectory = /app0/patches (becomes /app0/patches/SCUS-97129_config.lua) +// uint64_t LuaConfigDirectory = EBOOT(EBOOT_LUA_CONFIG_DIRECTORY); +// PS::Memory(LuaConfigDirectory).writeStdString(directory); + +// // LuaTrophyDirectory = /app0/trophy_data (becomes /app0/trophy_data/SCUS-97129_trophies.lua) +// uint64_t LuaTrophyDirectory = EBOOT(EBOOT_LUA_TROPHY_DIRECTORY); +// // PS::Memory(LuaTrophyDirectory).writeStdString(directory); + +// // LuaFeatureDirectory = /app0/feature_data (becomes /app0/feature_data/SCUS-97129_features.lua) +// uint64_t LuaFeatureDirectory = EBOOT(EBOOT_LUA_FEATURE_DIRECTORY); +// // PS::Memory(LuaFeatureDirectory).writeStdString(directory); + +// // LuaToolingDirectory = ./tooling/%(TITLE_ID) (becomes ./tooling/SCUS-97129/SCUS-97129_tooling.lua) +// uint64_t LuaToolingDirectory = EBOOT(EBOOT_LUA_TOOLING_DIRECTORY); +// // PS::Memory(LuaToolingDirectory).writeStdString(directory); + +// // LuaConfigLocalFile = ./config-local.lua +// // uint64_t LuaConfigLocalFile = EBOOT(EBOOT_LUA_LOCAL_CONFIG_FILE); + +// PS::ReloadLuaScripts(); +// } + +void PS::EmuSendCommand(uint32_t emuMsgId, uint64_t arg) +{ + // Setup command + uint64_t ptr = PS::Memory(EBOOT(EBOOT_EMU_COMMAND)).dereference()->dereference()->getAddress(); + + PS::Memory::write(ptr + 0x10, emuMsgId); + PS::Memory::write(ptr + 0x18, arg); + + // Trigger command + PS::Memory(EBOOT(EBOOT_EMU_COMMAND_COUNTER)).write(1); +} + +void PS::Snapshot_Save(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_Snapshot_Save, arg); +} + +void PS::Snapshot_SaveStamped(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_Snapshot_SaveStamped, arg); +} + +void PS::Snapshot_Restore(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_Snapshot_Restore, arg); +} + +void PS::Snapshot_SaveCyclic(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_Snapshot_SaveCyclic, arg); +} + +void PS::ResetJIT_EE(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_ResetJIT_EE, arg); +} + +void PS::ResetJIT_IOP(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_ResetJIT_IOP, arg); +} + +void PS::ResetJIT_VU(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_ResetJIT_VU, arg); +} + +void PS::ExitNicely(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_ExitNicely, arg); +} + +void PS::StopExec(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_StopExec, arg); +} + +void PS::StartExec(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_StartExec, arg); +} + +void PS::ToggleExec(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_ToggleExec, arg); +} + +void PS::StepExec(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_StepExec, arg); +} + +void PS::EnableToolingMode(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_EnableToolingMode, arg); +} + +void PS::GenCoreDump(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_GenCoreDump, arg); +} + +void PS::GsExternalCommand(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_GsExternalCommand, arg); +} + +void PS::RestorePoint_Save(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_RestorePoint_Save, arg); +} + +void PS::RestorePoint_Restore(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_RestorePoint_Restore, arg); +} + +void PS::StartVKLogging(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_StartVKLogging, arg); +} + +void PS::StopVKLogging(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_StopVKLogging, arg); +} + +void PS::SoftReset(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_SoftReset, arg); +} + +void PS::SwitchDisc(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_SwitchDisc, arg); +} + +void PS::SwitchDiscSwitch(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_SwitchDiscSwitch, arg); +} + +void PS::SwitchDiscClose(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_SwitchDiscClose, arg); +} + +void PS::ReloadLuaScripts(uint64_t arg) +{ + PS::EmuSendCommand(uxMsg_ReloadLuaScripts, arg); } int32_t PS::readAll(int32_t fd, void* buf, size_t len) diff --git a/sdk/src/ps/sce/usb/filesystems/exfat/boot-sector.cpp b/sdk/src/ps/sce/usb/filesystems/exfat/boot-sector.cpp new file mode 100644 index 0000000..55920bc --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/exfat/boot-sector.cpp @@ -0,0 +1 @@ +#include diff --git a/sdk/src/ps/sce/usb/filesystems/exfat/directory.cpp b/sdk/src/ps/sce/usb/filesystems/exfat/directory.cpp new file mode 100644 index 0000000..638495b --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/exfat/directory.cpp @@ -0,0 +1,14 @@ +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +exFAT::Directory::Directory() : exFAT::Record::Record() +{ + +} + +exFAT::Directory::Directory(char* name, exFAT::FileDirectoryEntry entry, exFAT::StreamExtensionEntry stream, uint32_t bytesPerCluster) : exFAT::Record::Record(name, entry, stream, bytesPerCluster) +{ + +} +#endif +#endif \ No newline at end of file diff --git a/sdk/src/ps/sce/usb/filesystems/exfat/entry.cpp b/sdk/src/ps/sce/usb/filesystems/exfat/entry.cpp new file mode 100644 index 0000000..d68cdc7 --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/exfat/entry.cpp @@ -0,0 +1 @@ +#include diff --git a/sdk/src/ps/sce/usb/filesystems/exfat/file.cpp b/sdk/src/ps/sce/usb/filesystems/exfat/file.cpp new file mode 100644 index 0000000..d38b8b7 --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/exfat/file.cpp @@ -0,0 +1,25 @@ +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include + +exFAT::File::File() : exFAT::Record::Record() +{ + +} + +exFAT::File::File(char* name, exFAT::FileDirectoryEntry entry, exFAT::StreamExtensionEntry stream, uint32_t bytesPerCluster) : exFAT::Record::Record(name, entry, stream, bytesPerCluster) +{ + +} + +bool exFAT::File::hasExtension(const char* extension) +{ + char* filename = this->getName(); + int index = PS2::lastIndexOf(filename, '.'); + if (index == -1) + return false; + return PS2::strcmp((filename + index + 1), extension) == 0; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/src/ps/sce/usb/filesystems/exfat/filesystem.cpp b/sdk/src/ps/sce/usb/filesystems/exfat/filesystem.cpp new file mode 100644 index 0000000..95f7ed2 --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/exfat/filesystem.cpp @@ -0,0 +1,426 @@ +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include +#include + +exFAT::Filesystem::Filesystem() +{ + this->mounted = false; + this->massStore = nullptr; + this->baseAddress = 0; + this->clusterSize = 0; + this->fatSize = 0; + this->fatBase = 0; + this->dataBase = 0; + + this->cacheIndex = 0xFFFFFFFF; + + this->readStartClusterIndex = 0; + this->readClusterIndex = 0; +} + +exFAT::Filesystem::Filesystem(PS::MassStore* massStore, uint32_t baseAddress, exFAT::BootSector bootSector) +{ + this->mounted = false; + this->massStore = massStore; + this->baseAddress = baseAddress; + this->bootSector = bootSector; + this->clusterSize = 0; + this->fatSize = 0; + this->fatBase = 0; + this->dataBase = 0; + + this->cacheIndex = 0xFFFFFFFF; + + this->readStartClusterIndex = 0; + this->readClusterIndex = 0; +} + +bool exFAT::Filesystem::isMounted() +{ + return this->mounted; +} + +bool exFAT::Filesystem::mount() +{ + if (this->isMounted()) + return true; + + if (this->massStore == nullptr) + return false; + + // Bytes per sector must be 512 + if (this->bootSector.getBytesPerSector() != SECTOR_SIZE) + return false; + + // 1 FAT must be present for removable media + if (this->bootSector.numberOfFATs != 1) + return false; + + this->clusterSize = this->bootSector.getClusterSize(); + this->fatSize = this->bootSector.getFatSize(); + this->fatBase = this->bootSector.getFatBase(); + this->dataBase = this->bootSector.getDataBase(); + + this->mounted = true; + return true; +} + +uint32_t exFAT::Filesystem::getClusterSize() +{ + return this->clusterSize; +} + +bool exFAT::Filesystem::root(List* directories, List* files) +{ + return this->directory(this->bootSector.rootCluster, directories, files); +} + +bool exFAT::Filesystem::directory(const char* filepath, List* directories, List* files) +{ + if (filepath == "/") + return this->root(directories, files); + + exFAT::Directory parent; + if (!this->getDirectoryFromFilepath(filepath, &parent)) + return false; + + if (this->directory(parent, directories, files)) + return true; + return false; +} + +bool exFAT::Filesystem::directory(exFAT::Directory parent, List* directories, List* files) +{ + return this->directory(parent.getCluster(), directories, files); +} + +List exFAT::Filesystem::directories(const char* filepath) +{ + List directories; + this->directory(filepath, &directories); + return directories; +} + +List exFAT::Filesystem::directories(exFAT::Directory parent) +{ + List directories; + this->directory(parent, &directories); + return directories; +} + +List exFAT::Filesystem::files(const char* filepath) +{ + List files; + this->directory(filepath, nullptr, &files); + return files; +} + +List exFAT::Filesystem::files(exFAT::Directory parent) +{ + List files; + this->directory(parent, nullptr, &files); + return files; +} + +bool exFAT::Filesystem::directory(uint32_t cluster, List* directories, List* files) +{ + if (!this->isMounted() || (directories == nullptr && files == nullptr)) + return false; + + uint32_t bytesPerCluster = (uint64_t)this->bootSector.getClusterSize(); + + // Loop each sector in cluster + for (uint32_t i = 0; i < this->bootSector.getSectorsPerCluster(); i++) + { + // Read current sector in current cluster + uint8_t sector[SECTOR_SIZE]; + if (!this->readSector(this->getSectorIndexForCluster(cluster) + i, sector)) + continue; + + // Loop each directory entry + for (uint32_t j = 0; j < SECTOR_SIZE; j += sizeof(exFAT::Entry)) + { + // Get base entry information + exFAT::Entry baseEntry = *(exFAT::Entry*)(sector + j); + + // Ignore empty / unused entries / all types that are not file/directory + if (baseEntry.isEndOfDirectory() || baseEntry.isUnused() || baseEntry.getType() != EXFAT_ENTRY_TYPE_CODE_FILE_AND_DIRECTORY) + continue; + + // Get entry as file/directory + exFAT::FileDirectoryEntry entry = *(exFAT::FileDirectoryEntry*)(sector + j); + + // Ignore system files + if ((entry.fileAttributes & EXFAT_FILE_ATTRIBUTE_SYSTEM) == EXFAT_FILE_ATTRIBUTE_SYSTEM) + continue; + + // Move to next entry + j += sizeof(exFAT::Entry); + exFAT::Entry baseEntryStream = *(exFAT::Entry*)(sector + j); + + // Ignore empty / unused entries / or not stream extension + if (baseEntryStream.isEndOfDirectory() || baseEntryStream.isUnused() || baseEntryStream.getType() != EXFAT_ENTRY_TYPE_CODE_STREAM_EXTENSION) + continue; + + // Get entry as stream extension + exFAT::StreamExtensionEntry stream = *(exFAT::StreamExtensionEntry*)(sector + j); + + // Move to next entry + j += sizeof(exFAT::Entry); + exFAT::Entry baseEntryFilename = *(exFAT::Entry*)(sector + j); + + // Get filename entries + char filename[256]; + PS2::memset(filename, 0, sizeof(filename)); + while (!baseEntryFilename.isEndOfDirectory() && !baseEntryFilename.isUnused() && baseEntryFilename.getType() == EXFAT_ENTRY_TYPE_CODE_FILE_NAME) + { + exFAT::FilenameEntry filenameEntry = *(exFAT::FilenameEntry*)(sector + j); + + // Append filename + this->strcatwn(filename, filenameEntry.filename, sizeof(filenameEntry.filename)); + + j += sizeof(exFAT::Entry); + baseEntryFilename = *(exFAT::Entry*)(sector + j); + } + // Rewind one entry + j -= sizeof(exFAT::Entry); + + // Directory + if ((entry.fileAttributes & EXFAT_FILE_ATTRIBUTE_DIRECTORY) == EXFAT_FILE_ATTRIBUTE_DIRECTORY) + { + if (directories != nullptr) + directories->add(exFAT::Directory(filename, entry, stream, bytesPerCluster)); + continue; + } + + // File + if (files != nullptr) + files->add(exFAT::File(filename, entry, stream, bytesPerCluster)); + } + } + + // Sort directories by name + if (directories != nullptr) + this->sortDirectories(directories); + + // Sort files by name + if (files != nullptr) + this->sortFiles(files); + + return true; +} + +bool exFAT::Filesystem::getDirectoryFromFilepath(const char* filepath, exFAT::Directory* directory) +{ + if (directory == nullptr) + return false; + + char bFilepath[256]; + PS2::memset(bFilepath, 0, sizeof(bFilepath)); + PS2::strcpy(bFilepath, filepath); + uint32_t length = PS2::strlen(filepath); + + // Append trailing "/" + if (bFilepath[length - 1] != '/') + { + bFilepath[length] = '/'; + length++; + } + + uint32_t offsetStart = 0; + uint32_t offsetEnd = 0; + bool isRoot = true; + bool foundTarget = false; + + for (uint32_t i = 0; i < length; i++) + { + if (bFilepath[i] != '/') + continue; + + offsetEnd = i; + if (i != 0 && offsetEnd > offsetStart) + { + // Get sub directories + List directories; + if (isRoot) + { + if (!this->root(&directories, nullptr)) + return false; + isRoot = false; + } + else + { + if (!this->directory(*directory, &directories, nullptr)) + return false; + } + + // Find directory name in directories + bool found = false; + for (uint32_t j = 0; j < directories.size(); j++) + { + if (PS2::memcmp(directories[j].getName(), (void*)(bFilepath + offsetStart), offsetEnd - offsetStart) != 0) + continue; + *directory = directories[j]; + found = true; + foundTarget = true; + } + if (!found) + return false; + } + offsetStart = offsetEnd + 1; + } + return foundTarget; +} + +bool exFAT::Filesystem::exists(const char* filepath) +{ + if (filepath == "/") + return true; + + exFAT::Directory parent; + return this->getDirectoryFromFilepath(filepath, &parent); +} + +bool exFAT::Filesystem::resetRead(exFAT::File file) +{ + this->readStartClusterIndex = file.getCluster(); + this->readClusterIndex = this->readStartClusterIndex; + return this->readClusterIndex != EXFAT_END_OF_CLUSTER_CHAIN; +} + +bool exFAT::Filesystem::readNextCluster(exFAT::File file, uint8_t* buffer) +{ + if (this->readClusterIndex == EXFAT_END_OF_CLUSTER_CHAIN) + return false; + + if (file.isContinuous()) + { + uint32_t clusterCount = this->readClusterIndex - this->readStartClusterIndex; + if (clusterCount >= file.getClusterCount()) + return false; + } + + uint32_t sectorIndex = this->getSectorIndexForCluster(this->readClusterIndex); + bool valid = this->readCluster(sectorIndex, buffer); + + if (file.isContinuous()) + this->readClusterIndex++; + else + this->readClusterIndex = this->getNextCluster(this->readClusterIndex); + return valid; +} + +bool exFAT::Filesystem::readCluster(uint32_t blockOffset, uint8_t* buffer) +{ + // Read sectors in chunks + uint32_t sectorsPerCluster = this->bootSector.getSectorsPerCluster(); + uint32_t bytesPerSector = this->bootSector.getBytesPerSector(); + uint32_t offset = 0; + uint32_t chunkSize = 96; + for (uint32_t i = 0; i < sectorsPerCluster; i += chunkSize) + { + // Get remainder + uint32_t sectorsRemaining = chunkSize; + if (i + sectorsRemaining > sectorsPerCluster) + sectorsRemaining = sectorsPerCluster % sectorsRemaining; + if (!this->readSectors(blockOffset + i, buffer + offset, sectorsRemaining)) + return false; + offset += (bytesPerSector * sectorsRemaining); + } + return true; +} + +bool exFAT::Filesystem::cacheSector(uint32_t blockOffset) +{ + // Cache exists + if (blockOffset == this->cacheIndex) + return true; + + if (this->readSector(blockOffset, this->cache)) + { + this->cacheIndex = blockOffset; + return true; + } + return false; +} + +uint32_t exFAT::Filesystem::getNextCluster(uint32_t cluster) +{ + uint32_t offset = cluster * sizeof(uint32_t); + uint32_t sectorOffset = 0; + while (offset >= SECTOR_SIZE) + { + offset -= SECTOR_SIZE; + sectorOffset++; + } + + this->cacheSector(this->fatBase + sectorOffset); + return *(uint32_t*)(this->cache + offset); +} + +void exFAT::Filesystem::sortDirectories(List* directories) +{ + if (directories == nullptr) + return; + + for (uint32_t i = 0; i < directories->size() - 1; i++) + { + bool hasSwapped = false; + + for (uint32_t j = 0; j < directories->size() - i - 1; j++) + { + if (PS2::strcmp((*directories)[j].getName(), (*directories)[j + 1].getName()) > 0) + { + exFAT::Directory temp = (*directories)[j]; + (*directories)[j] = (*directories)[j + 1]; + (*directories)[j + 1] = temp; + + hasSwapped = true; + } + } + + if (!hasSwapped) + break; + } +} + +void exFAT::Filesystem::sortFiles(List* files) +{ + if (files == nullptr) + return; + + for (uint32_t i = 0; i < files->size() - 1; i++) + { + bool hasSwapped = false; + + for (uint32_t j = 0; j < files->size() - i - 1; j++) + { + if (PS2::strcmp((*files)[j].getName(), (*files)[j + 1].getName()) > 0) + { + exFAT::File temp = (*files)[j]; + (*files)[j] = (*files)[j + 1]; + (*files)[j + 1] = temp; + + hasSwapped = true; + } + } + + if (!hasSwapped) + break; + } +} + +char* exFAT::Filesystem::strcatwn(char* destination, uint8_t* source, uint32_t slen) +{ + uint32_t offset = PS2::strlen(destination); + for (uint32_t i = 0; i < slen; i += 2) + { + destination[offset] = source[i] <= 0x7f ? source[i] : '?'; + offset++; + } + return destination; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/src/ps/sce/usb/filesystems/exfat/record.cpp b/sdk/src/ps/sce/usb/filesystems/exfat/record.cpp new file mode 100644 index 0000000..eb69a7a --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/exfat/record.cpp @@ -0,0 +1,26 @@ +#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#include +#ifdef LIBKERNEL +#include + +exFAT::Record::Record() +{ + PS2::memset(this->name, 0, sizeof(this->name)); + this->clusterCount = 0; +} + +exFAT::Record::Record(char* name, exFAT::FileDirectoryEntry entry, exFAT::StreamExtensionEntry stream, uint32_t bytesPerCluster) +{ + PS2::memset(this->name, 0, sizeof(this->name)); + PS2::strcpy(this->name, name); + this->entry = entry; + this->stream = stream; + + this->clusterCount = 0; + for (uint64_t i = 0; i < this->stream.length; i += bytesPerCluster) + this->clusterCount++; + if (this->stream.length > 0) + this->clusterCount++; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/src/ps/sce/usb/filesystems/mbr/master-boot-record-table.cpp b/sdk/src/ps/sce/usb/filesystems/mbr/master-boot-record-table.cpp new file mode 100644 index 0000000..a84539e --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/mbr/master-boot-record-table.cpp @@ -0,0 +1 @@ +#include diff --git a/sdk/src/ps/sce/usb/filesystems/mbr/master-boot-record.cpp b/sdk/src/ps/sce/usb/filesystems/mbr/master-boot-record.cpp new file mode 100644 index 0000000..5cca439 --- /dev/null +++ b/sdk/src/ps/sce/usb/filesystems/mbr/master-boot-record.cpp @@ -0,0 +1 @@ +#include diff --git a/sdk/src/ps/sce/usb/mass-store.cpp b/sdk/src/ps/sce/usb/mass-store.cpp new file mode 100644 index 0000000..7fd74b7 --- /dev/null +++ b/sdk/src/ps/sce/usb/mass-store.cpp @@ -0,0 +1,153 @@ +#if (defined(PS4) && PS4) +#include +#ifdef LIBKERNEL +#include +#include +#include + +uint32_t PS::MassStore::tag = 1; + +PS::MassStore::MassStore() +{ + this->usbd = nullptr; + this->endpointIn = 0; + this->endpointOut = 0; +} + +PS::MassStore::MassStore(PS::Sce::Usbd* usbd, uint8_t endpointIn, uint8_t endpointOut) +{ + this->usbd = usbd; + this->endpointIn = endpointIn; + this->endpointOut = endpointOut; +} + +int32_t PS::MassStore::inquiry(SCSI::Inquiry* inquiry) +{ + SCSI::CommandBlock command = { + .signature = MASS_STORE_CBW_SIGNATURE, + .dataTransferLength = sizeof(SCSI::Inquiry), + .flags = MASS_STORE_COMMAND_DIRECTION_DATA_IN, + .lun = 0, + .scsiCommandLength = 6, + .scsiCommandData = { SCSI_CMD_INQUIRY, 0x00, 0x00, 0x00, sizeof(SCSI::Inquiry), 0x00 } + }; + + int32_t err = this->command(&command, (uint8_t*)inquiry); + if (err != SCE_OK) + return err; + + SCSI::CommandStatus status; + return this->getReturnedStatus(&status); +} + +int32_t PS::MassStore::readCapacity(SCSI::Capacity* capacity) +{ + SCSI::CommandBlock command = { + .signature = MASS_STORE_CBW_SIGNATURE, + .dataTransferLength = sizeof(SCSI::Capacity), + .flags = MASS_STORE_COMMAND_DIRECTION_DATA_IN, + .lun = 0, + .scsiCommandLength = 10, + .scsiCommandData = { SCSI_CMD_READ_CAPACITY_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }; + + int32_t err = this->command(&command, (uint8_t*)capacity); + if (err != SCE_OK) + return err; + + // Swap Endian + capacity->blocks = PS::Memory::swapEndian(capacity->blocks); + capacity->blockSize = PS::Memory::swapEndian(capacity->blockSize); + + SCSI::CommandStatus status; + return this->getReturnedStatus(&status); +} + +int32_t PS::MassStore::readBlock(uint32_t blockAddress, uint16_t blocks, uint16_t blockSize, uint8_t* buffer) +{ + SCSI::CommandBlock command = { + .signature = MASS_STORE_CBW_SIGNATURE, + .dataTransferLength = (uint32_t)blocks * (uint32_t)blockSize, + .flags = MASS_STORE_COMMAND_DIRECTION_DATA_IN, + .lun = 0, + .scsiCommandLength = 10, + .scsiCommandData = { + SCSI_CMD_READ_10, + 0x00, + (uint8_t)(blockAddress >> 24), // MSB of Block Address + (uint8_t)(blockAddress >> 16), + (uint8_t)(blockAddress >> 8), + (uint8_t)(blockAddress & 0xFF), + 0x00, + (uint8_t)(blocks >> 8), // MSB Blocks to read + (uint8_t)(blocks & 0xFF), // LSB Blocks to read + 0x00 + } + }; + + int32_t err = this->command(&command, buffer); + if (err != SCE_OK) + return err; + + SCSI::CommandStatus status; + return this->getReturnedStatus(&status); +} + +int32_t PS::MassStore::command(SCSI::CommandBlock* command, uint8_t* buffer) +{ + if (this->usbd == nullptr) + return SCE_USBD_ERROR_NO_DEVICE; + + // Set unique tag + command->tag = PS::MassStore::tag; + PS::MassStore::tag++; + + // Send data + int32_t err = this->usbd->Send(this->getEndpointOut(), (uint8_t*)command, sizeof(SCSI::CommandBlock)); + if (err != SCE_OK) + return err; + + // Receive data + if (buffer != nullptr) + return this->sendReceiveData(command, buffer); + return SCE_OK; +} + +int32_t PS::MassStore::sendReceiveData(SCSI::CommandBlock* command, uint8_t* buffer) +{ + if (this->usbd == nullptr) + return SCE_USBD_ERROR_NO_DEVICE; + + uint16_t dataTransferLength = command->dataTransferLength; + + // Receive + if (command->flags & MASS_STORE_COMMAND_DIRECTION_DATA_IN) + { + int32_t received = 0; + return this->usbd->Receive(this->getEndpointIn(), buffer, dataTransferLength, &received); + } + + // Send + return this->usbd->Send(this->getEndpointOut(), buffer, dataTransferLength); +} + +int32_t PS::MassStore::getReturnedStatus(SCSI::CommandStatus* status) +{ + if (this->usbd == nullptr) + return SCE_USBD_ERROR_NO_DEVICE; + + int32_t received = 0; + return this->usbd->Receive(this->getEndpointIn(), (uint8_t*)status, sizeof(SCSI::CommandStatus), &received); +} + +uint8_t PS::MassStore::getEndpointIn() +{ + return this->endpointIn != 0 ? this->endpointIn : MASS_STORE_BULK_ONLY_DEFAULT_ENDPOINT_IN; +} + +uint8_t PS::MassStore::getEndpointOut() +{ + return this->endpointOut != 0 ? this->endpointOut : MASS_STORE_BULK_ONLY_DEFAULT_ENDPOINT_OUT; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/src/ps/sce/usb/usb/usb.cpp b/sdk/src/ps/sce/usb/usb/usb.cpp new file mode 100644 index 0000000..cdac48e --- /dev/null +++ b/sdk/src/ps/sce/usb/usb/usb.cpp @@ -0,0 +1,434 @@ +#if (defined(PS4) && PS4) +#include +#ifdef LIBKERNEL +#include +#include +#include +#include +#include + +uint64_t Usb::devices = 0; + +List Usb::list() +{ + // Free any previous allocations + if (Usb::devices != 0) + { + PS::Sce::Usbd::FreeDeviceList(Usb::devices, 1); + Usb::devices = 0; + } + + List usbs = List(); + + uint32_t deviceCount = (uint32_t)PS::Sce::Usbd::GetDeviceList(&Usb::devices); + for (uint32_t i = 0; i < deviceCount; i++) + { + uint64_t device = PS::Memory::read(Usb::devices, i); + usbs.add(Usb(device)); + } + return usbs; +} + +Usb::Usb() +{ + this->device = 0; + this->vid = 0; + this->pid = 0; + this->endpointIn = 0; + this->endpointOut = 0; + this->usbd = PS::Sce::Usbd(); + this->massStore = PS::MassStore(&this->usbd); + this->mounted = false; + this->filesystem = exFAT::Filesystem(); + + PS2::memset(this->vendor, 0x00, sizeof(this->vendor)); + PS2::memset(this->product, 0x00, sizeof(this->product)); + + this->sectorCount = 0; + this->sectorSize = 0; +} + +Usb::Usb(uint64_t device) +{ + this->device = device; + this->vid = 0; + this->pid = 0; + this->endpointIn = 0; + this->endpointOut = 0; + this->usbd = PS::Sce::Usbd(); + this->massStore = PS::MassStore(&this->usbd); + this->mounted = false; + this->filesystem = exFAT::Filesystem(); + + PS2::memset(this->vendor, 0x00, sizeof(this->vendor)); + PS2::memset(this->product, 0x00, sizeof(this->product)); + + this->sectorCount = 0; + this->sectorSize = 0; +} + +Usb::Usb(uint16_t vid, uint16_t pid) +{ + this->device = 0; + this->vid = vid; + this->pid = pid; + this->endpointIn = 0; + this->endpointOut = 0; + this->usbd = PS::Sce::Usbd(this->vid, this->pid); + this->massStore = PS::MassStore(&this->usbd); + this->mounted = false; + this->filesystem = exFAT::Filesystem(); + + PS2::memset(this->vendor, 0x00, sizeof(this->vendor)); + PS2::memset(this->product, 0x00, sizeof(this->product)); + + this->sectorCount = 0; + this->sectorSize = 0; +} + +bool Usb::isOpen() +{ + return this->usbd.IsOpen(); +} + +bool Usb::open() +{ + if (this->vid == 0 && this->pid == 0) + { + if (!this->updateDescriptors()) + return false; + } + + if (this->isOpen()) + return true; + + return this->usbd.Open(); +} + +bool Usb::updateDescriptors() +{ + if (this->device == 0) + return false; + + PS::Sce::Usbd::DeviceDescriptor descriptor; + if (PS::Sce::Usbd::GetDeviceDescriptor(this->device, &descriptor) != SCE_OK) + return false; + + if (descriptor.descriptorType != LIBUSB_DT_DEVICE) + return false; + + PS::Debug.printf("Device #%x\n", this->device); + PS::Debug.printf(" Type: 0x%02x\n", descriptor.descriptorType); + PS::Debug.printf(" Vendor Id: 0x%04x\n", descriptor.idVendor); + PS::Debug.printf(" Product Id: 0x%04x\n", descriptor.idProduct); + PS::Debug.printf(" Class: 0x%02x\n", descriptor.deviceClass); + PS::Debug.printf(" Sub Class: 0x%02x\n", descriptor.deviceSubClass); + PS::Debug.printf(" Protocol: 0x%02x\n", descriptor.deviceProtocol); + PS::Debug.printf(" Manufacturer: 0x%02x\n", descriptor.manufacturer); + PS::Debug.printf(" Product: 0x%02x\n", descriptor.product); + PS::Debug.printf(" Serial Number: 0x%02x\n", descriptor.serialNumber); + + bool isMassStorage = false; + + // Loop configurations + PS::Debug.printf(" Configurations (%i)\n", descriptor.numConfigurations); + for (uint32_t i = 0; i < descriptor.numConfigurations; i++) + { + // Get config descriptor + uint64_t configPtr = 0; + if (PS::Sce::Usbd::GetConfigDescriptor(this->device, (uint8_t)i, &configPtr) != SCE_OK) + continue; + + // Dereference config descriptor pointer + PS::Sce::Usbd::ConfigDescriptor config = PS::Memory::read(configPtr); + + if (config.descriptorType != LIBUSB_DT_CONFIG) + continue; + + PS::Debug.printf(" Config #%i\n", i + 1); + PS::Debug.printf(" Type: 0x%02x\n", config.descriptorType); + PS::Debug.printf(" Value: 0x%02x\n", config.configurationValue); + PS::Debug.printf(" Configuration: 0x%02x\n", config.configuration); + PS::Debug.printf(" Attributes: 0x%02x\n", config.attributes); + PS::Debug.printf(" Max Power: 0x%02x\n", config.maxPower); + + // Loop interfaces + PS::Debug.printf(" Interfaces (%i)\n", config.numInterfaces); + for (uint32_t j = 0; j < config.numInterfaces; j++) + { + PS::Sce::Usbd::Interface interface = PS::Memory::read(config.interface, j); + + PS::Debug.printf(" Interface #%i\n", j + 1); + + // Loop interface settings + PS::Debug.printf(" Alternate Settings (%i)\n", config.numInterfaces); + for (uint32_t k = 0; k < interface.numAltSetting; k++) + { + PS::Sce::Usbd::InterfaceDescriptor interfaceDescriptor = PS::Memory::read(interface.altSetting, k); + + PS::Debug.printf(" Alternate Setting: #%i\n", k + 1); + PS::Debug.printf(" Number: %i\n", interfaceDescriptor.interfaceNumber); + PS::Debug.printf(" Type: 0x%02x\n", interfaceDescriptor.descriptorType); + PS::Debug.printf(" Setting: 0x%02x\n", interfaceDescriptor.alternateSetting); + PS::Debug.printf(" Class: 0x%02x\n", interfaceDescriptor.interfaceClass); + PS::Debug.printf(" Sub Class: 0x%02x\n", interfaceDescriptor.interfaceSubClass); + PS::Debug.printf(" Protocol: 0x%02x\n", interfaceDescriptor.interfaceProtocol); + PS::Debug.printf(" Interface: 0x%02x\n", interfaceDescriptor.interface); + + // Check interface is mass storage + if (interfaceDescriptor.interfaceClass == LIBUSB_CLASS_MASS_STORAGE && + (interfaceDescriptor.interfaceSubClass == 0x01 || interfaceDescriptor.interfaceSubClass == 0x06) && + interfaceDescriptor.interfaceProtocol == 0x50) + isMassStorage = true; + + // Loop endpoints + PS::Debug.printf(" Endpoints (%i)\n", interfaceDescriptor.numEndpoints); + for (uint32_t l = 0; l < interfaceDescriptor.numEndpoints; l++) + { + PS::Sce::Usbd::EndpointDescriptor endpoint = PS::Memory::read(interfaceDescriptor.endpoint, l); + PS::Debug.printf(" Endpoint: #%i\n", l + 1); + PS::Debug.printf(" Type: 0x%02x\n", endpoint.descriptorType); + PS::Debug.printf(" Address: 0x%02x\n", endpoint.endpointAddress); + PS::Debug.printf(" Attributes: 0x%02x\n", endpoint.attributes); + + if ((endpoint.attributes & LIBUSB_TRANSFER_TYPE_MASK) & (LIBUSB_TRANSFER_TYPE_BULK | LIBUSB_TRANSFER_TYPE_INTERRUPT)) + { + if (endpoint.endpointAddress & LIBUSB_ENDPOINT_IN) + this->endpointIn = this->endpointIn ? this->endpointIn : endpoint.endpointAddress; + else + this->endpointOut = this->endpointOut ? this->endpointOut : endpoint.endpointAddress; + } + } + } + } + + // Free config descriptor + PS::Sce::Usbd::FreeConfigDescriptor(configPtr); + } + + if (!isMassStorage) + return false; + + this->vid = descriptor.idVendor; + this->pid = descriptor.idProduct; + + if (this->usbd.IsOpen()) + this->usbd.Close(); + + this->usbd = PS::Sce::Usbd(this->vid, this->pid); + this->massStore = PS::MassStore(&this->usbd, this->endpointIn, this->endpointOut); + return true; +} + +bool Usb::mount() +{ + if (this->isMounted()) + return true; + + if (!this->isOpen() && !this->open()) + return false; + + PS::Debug.printf("Mounting USB: VID=%04x, PID=%04x\n", this->vid, this->pid); + + SCSI::Inquiry inquiry; + if (this->massStore.inquiry(&inquiry) != SCE_OK) + return false; + + PS2::strcpy(this->vendor, (const char*)inquiry.vendorID); + this->vendor[sizeof(this->vendor) - 1] = '\0'; + PS2::strcpy(this->product, (const char*)inquiry.productID); + this->product[sizeof(this->product) - 1] = '\0'; + + PS::Debug.printf("Vendor: %s\n", this->vendor); + PS::Debug.printf("Product: %s\n", this->product); + + // Get capacity + SCSI::Capacity capacity; + if (this->massStore.readCapacity(&capacity) != SCE_OK) + return false; + + this->sectorCount = capacity.blocks; + this->sectorSize = capacity.blockSize; + + PS::Debug.printf("Sector Count: 0x%x\n", this->sectorCount); + PS::Debug.printf("Sector Size: %u\n", this->sectorSize); + + // Only support fixed sector size + if (this->sectorSize != SECTOR_SIZE) + return false; + + // Read master boot record table + MasterBootRecordTable mbr; + if (!this->readSector(0, (uint8_t*)&mbr)) + { + PS::Debug.printf("Failed to read master boot record\n"); + return false; + } + + // Validate master boot record + if (!mbr.isValid()) + { + PS::Debug.printf("Master boot record is invalid\n"); + return false; + } + + // Loop each partition + bool filesystemFound = false; + for (uint32_t i = 0; i < mbr.getMaxParitionCount(); i++) + { + MasterBootRecord partition = mbr.getParition(i); + if (partition.isEmpty()) + continue; + + // Determine partition filesystem type + uint8_t bootSector[SECTOR_SIZE]; + if (!this->readSector(partition.logicalBlockAddress, bootSector)) + continue; + + // https://en.wikipedia.org/wiki/BIOS_parameter_block + + // exFAT + if (PS2::memcmp(bootSector + 0x03, (void*)"EXFAT ", 8) == 0) + { + this->partition = partition; + this->filesystem = exFAT::Filesystem(&this->massStore, this->partition.logicalBlockAddress, *(exFAT::BootSector*)bootSector); + filesystemFound = true; + break; + } + + // Unhandled filesystem + continue; + } + + // Failed to find filesystem + if (!filesystemFound) + { + PS::Debug.printf("Failed to find exFAT filesystem\n"); + return false; + } + + // Mount filesystem + this->mounted = this->filesystem.mount(); + return this->mounted; +} + +void Usb::unmount() +{ + if (!this->isMounted()) + return; + this->mounted = false; +} + +bool Usb::isMounted() +{ + return this->mounted; +} + +void Usb::close() +{ + if (this->usbd.IsOpen()) + this->usbd.Close(); +} + +uint32_t Usb::getClusterSize() +{ + if (!this->isMounted()) + return 0; + + return this->filesystem.getClusterSize(); +} + +bool Usb::root(List* directories, List* files) +{ + if (!this->isMounted()) + return false; + + return this->filesystem.root(directories, files); +} + +bool Usb::directory(const char* filepath, List* directories, List* files) +{ + if (!this->isMounted()) + return false; + + return this->filesystem.directory(filepath, directories, files); +} + +bool Usb::directory(exFAT::Directory parent, List* directories, List* files) +{ + if (!this->isMounted()) + return false; + + return this->filesystem.directory(parent, directories, files); +} + +List Usb::directories(const char* filepath) +{ + if (!this->isMounted()) + return List(); + + return this->filesystem.directories(filepath); +} + +List Usb::directories(exFAT::Directory parent) +{ + if (!this->isMounted()) + return List(); + + return this->filesystem.directories(parent); +} + +List Usb::files(const char* filepath) +{ + if (!this->isMounted()) + return List(); + + return this->filesystem.files(filepath); +} + +List Usb::files(exFAT::Directory parent) +{ + if (!this->isMounted()) + return List(); + + return this->filesystem.files(parent); +} + +bool Usb::exists(const char* filepath) +{ + if (!this->isMounted()) + return false; + + return this->filesystem.exists(filepath); +} + +bool Usb::resetRead(exFAT::File file) +{ + if (!this->isMounted()) + return false; + + return this->filesystem.resetRead(file); +} + +bool Usb::readNextCluster(exFAT::File file, uint8_t* buffer) +{ + if (!this->isMounted()) + return false; + + return this->filesystem.readNextCluster(file, buffer); +} + +bool Usb::readSector(uint32_t blockAddress, uint8_t* buffer) +{ + return this->readSectors(blockAddress, buffer, 1); +} + +bool Usb::readSectors(uint32_t blockAddress, uint8_t* buffer, uint32_t count) +{ + int error = this->massStore.readBlock(blockAddress, count, this->sectorSize, buffer); + if (error != SCE_OK) + PS::Debug.printf("Mass Store read block failed with error 0x%x\n", error); + return error == SCE_OK; +} +#endif +#endif \ No newline at end of file diff --git a/sdk/src/ps/sce/usbd.cpp b/sdk/src/ps/sce/usb/usbd.cpp similarity index 91% rename from sdk/src/ps/sce/usbd.cpp rename to sdk/src/ps/sce/usb/usbd.cpp index 7e945c0..b7cfce4 100644 --- a/sdk/src/ps/sce/usbd.cpp +++ b/sdk/src/ps/sce/usb/usbd.cpp @@ -1,8 +1,8 @@ -#if (defined(PS4) && PS4) || (defined(PS5) && PS5) +#if (defined(PS4) && PS4) #include #ifdef LIBKERNEL #include -#include +#include bool PS::Sce::Usbd::isInitialized = false; int PS::Sce::Usbd::libUsbd = 0; @@ -62,9 +62,6 @@ bool PS::Sce::Usbd::Initialize() PS::Sce::Kernel::Dlsym(PS::Sce::Usbd::libUsbd, "sceUsbdGetConfigDescriptor", &PS::Sce::Usbd::pGetConfigDescriptor); PS::Sce::Kernel::Dlsym(PS::Sce::Usbd::libUsbd, "sceUsbdGetConfigDescriptorByValue", &PS::Sce::Usbd::pGetConfigDescriptorByValue); PS::Sce::Kernel::Dlsym(PS::Sce::Usbd::libUsbd, "sceUsbdFreeConfigDescriptor", &PS::Sce::Usbd::pFreeConfigDescriptor); - #elif defined(PS5) && PS5 - // TODO - Get base address, + offsets - // PS::Sce::Kernel::GetModuleInfo(PS::Sce::Usbd::libUsbd, &info); #endif PS::Sce::Usbd::Init(); @@ -101,12 +98,6 @@ int32_t PS::Sce::Usbd::GetDeviceDescriptor(uint64_t device, PS::Sce::Usbd::Devic return (int32_t)PS::Breakout::call(PS::Sce::Usbd::pGetDeviceDescriptor, device, PVAR_TO_NATIVE(desc)); } -int32_t PS::Sce::Usbd::Open(uint64_t device, uint64_t* handle) -{ - PS::Sce::Usbd::Initialize(); - return (int32_t)PS::Breakout::call(PS::Sce::Usbd::pOpen, device, PVAR_TO_NATIVE(handle)); -} - uint64_t PS::Sce::Usbd::OpenDeviceWithVidPid(uint16_t vendorId, uint16_t productId) { PS::Sce::Usbd::Initialize(); @@ -131,27 +122,36 @@ int32_t PS::Sce::Usbd::GetConfigDescriptorByValue(uint64_t device, uint8_t bConf return (int32_t)PS::Breakout::call(PS::Sce::Usbd::pGetConfigDescriptorByValue, device, bConfigurationValue, PVAR_TO_NATIVE(config)); } -void PS::Sce::Usbd::FreeConfigDescriptor(Sce::Usbd::ConfigDescriptor* config) +void PS::Sce::Usbd::FreeConfigDescriptor(uint64_t /* Sce::Usbd::ConfigDescriptor* */ config) { PS::Sce::Usbd::Initialize(); - PS::Breakout::call(PS::Sce::Usbd::pFreeConfigDescriptor, PVAR_TO_NATIVE(config)); + PS::Breakout::call(PS::Sce::Usbd::pFreeConfigDescriptor, config); } PS::Sce::Usbd::Usbd() { this->handle = 0; + this->vendorId = 0; + this->productId = 0; } -PS::Sce::Usbd::Usbd(uint64_t device) +PS::Sce::Usbd::Usbd(uint16_t vendorId, uint16_t productId) { - PS::Sce::Usbd::Initialize(); - PS::Sce::Usbd::Open(device, &this->handle); + this->handle = 0; + this->vendorId = vendorId; + this->productId = productId; } -PS::Sce::Usbd::Usbd(uint16_t vendorId, uint16_t productId) +bool PS::Sce::Usbd::Open() { + if (this->vendorId == 0 && this->productId == 0) + return false; + PS::Sce::Usbd::Initialize(); - this->handle = PS::Sce::Usbd::OpenDeviceWithVidPid(vendorId, productId); + if (this->IsOpen()) + return true; + this->handle = PS::Sce::Usbd::OpenDeviceWithVidPid(this->vendorId, this->productId); + return this->IsOpen(); } void PS::Sce::Usbd::Close()