├── .gitignore ├── .github └── FUNDING.yml ├── src ├── downloader │ ├── Downloader.hpp │ └── Downloader.cpp └── main.cpp ├── README.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | 3 | # Build objects 4 | build/ 5 | bin/ 6 | .vscode/ -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [McCaulay] 4 | custom: ['https://www.buymeacoffee.com/mccaulay'] 5 | -------------------------------------------------------------------------------- /src/downloader/Downloader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "mast1c0re.hpp" 5 | 6 | #define MAGIC 0x0000EA6E 7 | #define MAX_DOWNLOAD_CHUNK_SIZE 0xFFFF 8 | #define DOWNLOAD_BAR_UPDATE_FREQUENCY 1500 9 | 10 | class Downloader 11 | { 12 | public: 13 | static bool download(const char* filepath, const char* fileType, uint16_t port, uint16_t chunkSize = 0x1000); 14 | private: 15 | static void setProgress(PS::Sce::MsgDialogProgressBar dialog, size_t downloaded, size_t total); 16 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mast1c0re - PS2 Network Game Loader 2 | 3 | Load PS2 ISO games over the network using the [mast1c0re](https://github.com/McCaulay/mast1c0re) vulnerability. 4 | 5 | Requires the [mast1c0re-ps2-network-elf-loader](https://github.com/McCaulay/mast1c0re-ps2-network-elf-loader) game save to load the ELF file. 6 | 7 | Currently the sent game is stored temporarily on disk, and is removed once the game is closed. Therefore, it requires you to transfer the ISO each time you load the game loader. 8 | 9 | ## Sending an ISO 10 | You can send an ISO file by using one of the following methods: 11 | * (GUI exe) Download "[mast1c0re-file-loader.exe](https://github.com/mast1c0re-ps2-network-elf-loader/releases/download/v0.1.1/mast1c0re-file-loader.exe)" in [releases](https://github.com/McCaulay/mast1c0re-ps2-network-elf-loader/tags) 12 | * (Python GUI) python3 [mast1c0re-file-loader.py](https://github.com/mast1c0re-ps2-network-elf-loader/blob/master/scripts/mast1c0re-file-loader.py) 13 | * (Command Line) python3 [mast1c0re-send-file.py](https://github.com/mast1c0re-ps2-network-elf-loader/blob/master/scripts/mast1c0re-send-file.py) --ip --file 14 | * (GUI) [OkageLibrary](https://github.com/SvenGDK/OkageLibrary/releases) 15 | 16 | ## Supported Systems & Firmware Versions 17 | * PS4 18 | * 5.05 19 | * 6.72 20 | * 9.00 21 | * 10.01 22 | * 10.50 23 | * 10.70 24 | * 10.71 25 | * PS5 26 | * 6.50 27 | 28 | ## Improvements 29 | * Emulator load lua file 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBMAST1C0RE=$(MAST1C0RE)/sdk 2 | 3 | # Addresses 4 | TEXT ?= 0x1300000 5 | DATA ?= 0x1110000 6 | ABI ?= 0x1100000 7 | 8 | # Variables 9 | SYSTEM ?= PS4 10 | FIRMWARE ?= 0.00 11 | EBOOT_VERSION ?= 1.01 12 | 13 | FIRMWARE_UNDER = $(subst .,_,$(FIRMWARE)) 14 | FIRMWARE_DASH = $(subst .,-,$(FIRMWARE)) 15 | FIRMWARE_NUM = $(subst .,,$(FIRMWARE)) 16 | EBOOT_NUM = $(subst .,,$(EBOOT_VERSION)) 17 | 18 | # Binaries 19 | PREFIX = mips64r5900el-ps2-elf- 20 | CPP = $(PREFIX)g++ 21 | 22 | # Directories 23 | BDIR = bin 24 | ODIR = build 25 | SDIR = src 26 | 27 | # Files 28 | CPPFILES = $(wildcard $(SDIR)/*.cpp $(SDIR)/*/*.cpp) 29 | OBJS = $(patsubst $(SDIR)/%.cpp, $(ODIR)/%.o, $(CPPFILES)) 30 | 31 | # Flags 32 | LINKFLAGS = -Wl,-z,max-page-size=0x1,--section-start=.MIPS.abiflags=$(ABI) 33 | CPPFLAGS = -Tdata=$(DATA) -Ttext=$(TEXT) -mno-gpopt -nostartfiles -nostdlib -nodefaultlibs -ffreestanding $(LINKFLAGS) -I$(LIBMAST1C0RE)/include -I. -D$(SYSTEM)=1 -DFIRMWARE=$(FIRMWARE_NUM) -DEBOOT_VERSION=$(EBOOT_NUM) 34 | 35 | # Target 36 | TARGET = $(shell basename $(CURDIR))-$(SYSTEM)-$(FIRMWARE_DASH).elf 37 | 38 | all: compile 39 | 40 | compile: sdk $(ODIR) $(BDIR) $(OBJS) crt0 41 | $(CPP) $(CPPFLAGS) $(ODIR)/crt0.o $(OBJS) -L$(LIBMAST1C0RE) -l:mast1c0re.a -o $(BDIR)/$(TARGET) 42 | 43 | sdk: 44 | make -B -C $(LIBMAST1C0RE) SYSTEM=$(SYSTEM) FIRMWARE=$(FIRMWARE_NUM) EBOOT=$(EBOOT_NUM) clean 45 | make -B -C $(LIBMAST1C0RE) SYSTEM=$(SYSTEM) FIRMWARE=$(FIRMWARE_NUM) EBOOT=$(EBOOT_NUM) 46 | 47 | crt0: 48 | $(CPP) $(CPPFLAGS) -c $(LIBMAST1C0RE)/crt0.S -o $(ODIR)/crt0.o 49 | 50 | $(ODIR)/%.o: $(SDIR)/%.cpp 51 | @mkdir -p $(shell dirname $@) 52 | $(CPP) -c -o $@ $< $(CPPFLAGS) 53 | 54 | $(BDIR) $(ODIR): 55 | @mkdir -p $@ 56 | 57 | .PHONY: clean 58 | clean: 59 | rm -rf $(ODIR) 60 | 61 | .PHONY: clean_all 62 | clean_all: 63 | rm -rf $(BDIR)/*.elf $(ODIR) -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "downloader/Downloader.hpp" 3 | 4 | #define SERVER_PORT 9045 5 | #define DOWNLOAD_GAME_CHUNK_SIZE 0xF800 6 | #define DOWNLOAD_CONFIG_CHUNK_SIZE 10 7 | 8 | void main() 9 | { 10 | // Set pad light to purple 11 | PS::PadSetLightBar(150, 0, 255, 255); 12 | 13 | // Show "PS2 Game Loader" notification 14 | PS::notificationWithIcon("cxml://psnotification/tex_morpheus_trophy_platinum", "PS2 Game Loader (Network)"); 15 | 16 | // Attempt to connect to debug server 17 | // PS::Debug.connect(IP(192, 168, 0, 7), 9023); 18 | PS::Debug.printf("---------- Load PS2 Game (Network) ----------\n"); 19 | 20 | // Set paths 21 | const char* gameFilepath = "/av_contents/content_tmp/disc01.iso"; 22 | const char* configFilepath = "/av_contents/content_tmp/SCUS-97129_cli.conf"; 23 | bool hasConfig = false; 24 | 25 | // Download ISO if doesn't exist 26 | if (!PS::Filesystem::exists(gameFilepath)) 27 | { 28 | // Download ISO 29 | if (!Downloader::download(gameFilepath, "game", SERVER_PORT, DOWNLOAD_GAME_CHUNK_SIZE)) 30 | { 31 | PS::notification("Failed to download ISO"); 32 | PS::Debug.printf("Failed to download ISO\n"); 33 | 34 | // Disconnect from debug server 35 | PS::Debug.disconnect(); 36 | return; 37 | } 38 | } 39 | 40 | // Open a new dialog to ask for the config file 41 | if (PS::Sce::MsgDialogUserMessage::show("Do you want to load a config file?", PS::Sce::MsgDialog::ButtonType::YESNO)) 42 | { 43 | if (!PS::Filesystem::exists(configFilepath)) 44 | { 45 | // Download config 46 | if (!Downloader::download(configFilepath, "config", SERVER_PORT, DOWNLOAD_CONFIG_CHUNK_SIZE)) 47 | { 48 | // Failed to download config 49 | PS::notification("Failed to download config"); 50 | PS::Debug.printf("Failed to download config\n"); 51 | 52 | // Disconnect from debug server 53 | PS::Debug.disconnect(); 54 | return; 55 | } 56 | 57 | hasConfig = true; 58 | } 59 | } 60 | 61 | // Mount & Load iso 62 | PS::Debug.printf("Mounting...\n"); 63 | PS::MountDiscWithFilepath("./../av_contents/content_tmp/disc01.iso"); 64 | 65 | // Get game code from mounted file 66 | char* gameCode = PS::GetMountedGameCode(); 67 | if (PS2::strlen(gameCode) == 10) 68 | { 69 | // Convert name from "SCUS-97129" -> "cdrom0:\\SCUS_971.29;1" 70 | char* ps2Path = PS2::gameCodeToPath(gameCode); 71 | 72 | // Load configuration file 73 | if (hasConfig) 74 | { 75 | PS::Debug.printf("Processing config %s\n", configFilepath); 76 | PS::ProcessConfigFile("./../av_contents/content_tmp/SCUS-97129_cli.conf"); 77 | } 78 | 79 | // Disconnect from debug server 80 | PS::Debug.printf("Loading \"%s\"...\n", ps2Path); 81 | 82 | // Disconnect from debug server 83 | PS::Debug.disconnect(); 84 | 85 | // Restore corruption 86 | PS::Breakout::restore(); 87 | 88 | // Execute mounted iso 89 | PS2::LoadExecPS2(ps2Path, 0, NULL); 90 | return; 91 | } 92 | 93 | PS::notification("Unexpected game code \"%s\"!", gameCode); 94 | PS::Debug.printf("Unexpected game code (%s) length of %i, expecting %i\n", gameCode, PS2::strlen(gameCode), 10); 95 | 96 | // Disconnect from debug server 97 | PS::Debug.disconnect(); 98 | } 99 | -------------------------------------------------------------------------------- /src/downloader/Downloader.cpp: -------------------------------------------------------------------------------- 1 | #include "Downloader.hpp" 2 | 3 | bool Downloader::download(const char* filepath, const char* fileType, uint16_t port, uint16_t chunkSize) 4 | { 5 | // Create TCP server 6 | PS::TcpServer server = PS::TcpServer(); 7 | if (!server.listen(port)) 8 | return false; 9 | 10 | // Waiting for file dialog 11 | char waitingMessage[256]; 12 | PS2::sprintf(waitingMessage, "Waiting for %s file...", fileType); 13 | 14 | PS::Sce::MsgDialog::Initialize(); 15 | PS::Sce::MsgDialogUserMessage waitingDialog = PS::Sce::MsgDialogUserMessage(waitingMessage, PS::Sce::MsgDialog::ButtonType::NONE); 16 | waitingDialog.open(); 17 | 18 | // Accept connection 19 | PS::Debug.printf("Waiting for client connection...\n"); 20 | PS::TcpClient client = server.accept(); 21 | 22 | // File download variables 23 | size_t filesize = 0; 24 | size_t offset = 0; 25 | size_t headerSize = 0; 26 | 27 | // Check if file is sent with filesize 28 | uint8_t magic[sizeof(uint32_t)]; 29 | client.read(magic, sizeof(uint32_t)); 30 | offset += sizeof(uint32_t); 31 | if (*(uint32_t*)(magic) == MAGIC) 32 | { 33 | // Get filesize 34 | filesize = client.read(); 35 | offset += sizeof(size_t); 36 | PS::Debug.printf("Download file of size: %llu\n", filesize); 37 | 38 | headerSize = sizeof(uint32_t) + sizeof(size_t); 39 | } 40 | 41 | // Close waiting for file dialog 42 | waitingDialog.close(); 43 | PS::Sce::MsgDialog::Terminate(); 44 | 45 | // Show progress bar dialog 46 | char downloadingMessage[256]; 47 | PS2::sprintf(downloadingMessage, "Downloading %s file...", fileType); 48 | 49 | PS::Sce::MsgDialog::Initialize(); 50 | PS::Sce::MsgDialogProgressBar progressDialog = PS::Sce::MsgDialogProgressBar(downloadingMessage); 51 | PS::Sce::MsgDialogUserMessage staticDialog = PS::Sce::MsgDialogUserMessage(downloadingMessage, PS::Sce::MsgDialog::ButtonType::NONE); 52 | if (filesize != 0) 53 | { 54 | progressDialog.open(); 55 | Downloader::setProgress(progressDialog, 0, filesize); 56 | } 57 | else 58 | staticDialog.open(); 59 | 60 | // Write file to device 61 | PS::Debug.printf("Opening %s\n", filepath); 62 | int fd = PS::open(filepath, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG | S_IRWXO); 63 | if (fd > 0) 64 | { 65 | // Write first 4 bytes if it was not the magic value 66 | if (filesize == 0) 67 | PS::writeAll(fd, magic, 4); 68 | 69 | // Write file to disk from socket in chunks 70 | uint32_t updateBar = 0; 71 | char buffer[MAX_DOWNLOAD_CHUNK_SIZE]; 72 | while (true) 73 | { 74 | size_t readCount = client.read(buffer, chunkSize); 75 | offset += readCount; 76 | 77 | size_t writeCount = PS::writeAll(fd, buffer, readCount); 78 | if (writeCount != readCount) 79 | { 80 | PS::notification("Failed to write file to disk!"); 81 | PS::Debug.printf("Failed to write file, wrote %llu, expected to write %llu\n", writeCount, readCount); 82 | PS::close(fd); 83 | client.disconnect(); 84 | server.disconnect(); 85 | if (filesize != 0) 86 | { 87 | progressDialog.setValue(100); 88 | progressDialog.close(); 89 | } 90 | else 91 | staticDialog.close(); 92 | PS::Sce::MsgDialog::Terminate(); 93 | return false; 94 | } 95 | 96 | // End of download 97 | if (readCount != chunkSize) 98 | { 99 | PS::Debug.printf("Downloaded %lu bytes\n", offset); 100 | break; 101 | } 102 | 103 | // Update progress bar 104 | if (updateBar == DOWNLOAD_BAR_UPDATE_FREQUENCY) 105 | { 106 | if (filesize != 0) 107 | Downloader::setProgress(progressDialog, offset - headerSize, filesize); 108 | updateBar = 0; 109 | } 110 | updateBar++; 111 | } 112 | } 113 | 114 | // Disconnect 115 | PS::Debug.printf("Closing connection...\n"); 116 | PS::close(fd); 117 | client.disconnect(); 118 | server.disconnect(); 119 | 120 | if (filesize != 0) 121 | { 122 | progressDialog.setValue(100); 123 | progressDialog.close(); 124 | } 125 | else 126 | staticDialog.close(); 127 | PS::Sce::MsgDialog::Terminate(); 128 | 129 | return true; 130 | } 131 | 132 | void Downloader::setProgress(PS::Sce::MsgDialogProgressBar dialog, size_t downloaded, size_t total) 133 | { 134 | if (total == 0) 135 | return; 136 | 137 | // Calculate percentage without float/double 138 | uint64_t divident = downloaded * 100; 139 | uint64_t percentage = 0; 140 | 141 | while (divident >= total) 142 | { 143 | divident -= total; 144 | percentage++; 145 | } 146 | 147 | dialog.setValue(percentage); 148 | } 149 | --------------------------------------------------------------------------------