├── .gitignore ├── README.md ├── client ├── main.py └── requirements.txt └── plugin ├── Makefile ├── src ├── encoder.c └── main.c └── vitc.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *.skprx 2 | *.o 3 | *.d 4 | *.elf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 🔴 Project is no longer active 2 | 3 | # PS Vita - Stream to PC via Wifi (Beta) 4 | Resolution is currently 480x272, Low Quality is expected for beta version 5 | 6 | ## [Setup Tutorial](https://www.youtube.com/watch?v=TF5XHjNtOG8) 7 | 8 | ### Vita WiFi is Terrible 9 | 10 | The issue is not the WiFi chip but rather the firmware, It has a very limited network buffer (64KiB) 11 | 12 | For example I want to send a frame which is 960x544 in YUV format, The size will be: 13 | 14 | 960 * 544 * 3/2 = 783360 bytes 15 | 16 | If I send the data in a chunk of `8160` then: 17 | 18 | 783360 / 8160 = 96 messages 19 | 20 | And for each message I have to wait 1000us so that the buffer doesn't overflow 21 | 22 | That means I'm only getting ~10 FPS: 23 | 24 | 1s = 1000ms => 1000ms / 96ms = ~10 FPS 25 | 26 | Make sure no other app or plugin is using your network 27 | 28 | Overclocking is recommended for better FPS 29 | 30 | ## Todo 31 | 32 | - Use LZ4 compression to achieve native resolution with 30 FPS 33 | - Package the client (.exe, .dmg, .AppImage) 34 | - Improve resolution up to 720p 35 | - Android TV client 36 | - Audio Support 37 | - Config app for optional settings like turning display off 38 | - Add VP9 format to get +30 FPS 39 | 40 | ## Credit 41 | 42 | [xerpi/vita-udcd-uvc](https://github.com/xerpi/vita-udcd-uvc) 43 | -------------------------------------------------------------------------------- /client/main.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import re, sys, socket 3 | from time import sleep 4 | from threading import Thread 5 | from PyQt5.QtGui import QImage, QPixmap 6 | from PyQt5.QtCore import Qt, QThread, pyqtSignal 7 | from PyQt5.QtWidgets import QApplication, QStackedWidget, QMainWindow, QMessageBox, QWidget, QLabel, QVBoxLayout, QLineEdit, QPushButton 8 | 9 | 10 | IP = '' 11 | FPS = 0 12 | PORT = 54102 13 | BUFFER_SIZE = 8160 14 | FRAME_SIZE = 783360 15 | START_FRAME_CODE = b'G^kGtPhoMR0&Xj2k0z7P7@^0iM*#AL*UgzfEab$Gjhk@nzNGHse3sKHPW6U6KPqdrADB5p8KaEn9$Lq#LMyuata8fatqOj6Gd' 16 | END_FRAME_CODE = b'@aejW9QqBnsR07eaUHy&MF7bEY#d2sG&Q7e6$bw^XWohJyH1ri8bdOUTpxJy2nu@q8e9HiFwZl*wanNFFPKS&DABtVpQjbBH2hd' 17 | 18 | WIDTH = 480 19 | HEIGHT = 272 20 | 21 | 22 | class ConnectionThread(QThread): 23 | framebuf = b'' 24 | connectionEstablished = pyqtSignal() 25 | connectionUpdateFrame = pyqtSignal(bytes) 26 | connectionFailed = pyqtSignal(str) 27 | 28 | def __init__(self, IP, PORT): 29 | super().__init__() 30 | self.IP = IP 31 | self.PORT = PORT 32 | 33 | def run(self): 34 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 35 | sock.settimeout(5) 36 | 37 | try: 38 | sock.connect((self.IP, self.PORT)) 39 | 40 | sock.send("".encode()) # This is for the server to get client address 41 | data, addr = sock.recvfrom(BUFFER_SIZE) 42 | self.check_frame(data) 43 | 44 | self.connectionEstablished.emit() 45 | 46 | while True: 47 | data, addr = sock.recvfrom(BUFFER_SIZE) 48 | self.check_frame(data) 49 | except Exception as e: 50 | self.connectionFailed.emit(str(e)) 51 | finally: 52 | sock.close() 53 | 54 | def check_frame(self, data): 55 | if data == START_FRAME_CODE: 56 | self.framebuf = b'' 57 | elif data == END_FRAME_CODE: 58 | self.connectionUpdateFrame.emit(self.framebuf) 59 | else: 60 | self.framebuf += data 61 | 62 | 63 | class FrameWidget(QWidget): 64 | def __init__(self): 65 | super().__init__() 66 | 67 | self.label = QLabel() 68 | self.label.setScaledContents(True) 69 | self.label.setAlignment(Qt.AlignCenter) 70 | 71 | layout = QVBoxLayout() 72 | layout.setContentsMargins(0, 0, 0, 0) 73 | layout.addWidget(self.label) 74 | self.setLayout(layout) 75 | 76 | self.set_frame(b'\x00\x00\x00' * FRAME_SIZE, True) 77 | 78 | def nv12_to_rgb(self, frame, width=WIDTH, height=HEIGHT): 79 | from cv2 import cvtColor, COLOR_YUV2RGB_NV12 80 | 81 | yuv_frame = np.frombuffer(frame, dtype=np.uint8) 82 | yuv_frame = yuv_frame.reshape((height * 3 // 2, width)) 83 | rgb_frame = cvtColor(yuv_frame, COLOR_YUV2RGB_NV12) 84 | 85 | return rgb_frame 86 | 87 | def set_frame(self, frame, rgb=False): 88 | global FPS 89 | 90 | try: 91 | if not rgb: 92 | frame = bytearray(frame) 93 | frame = self.nv12_to_rgb(frame) 94 | image = QImage(frame, WIDTH, HEIGHT, QImage.Format_RGB888) 95 | pixmap = QPixmap.fromImage(image).scaled(self.size()) 96 | self.label.setPixmap(pixmap) 97 | FPS += 1 98 | except: 99 | ... 100 | 101 | 102 | class MainWindow(QMainWindow): 103 | def __init__(self): 104 | super().__init__() 105 | self.setWindowTitle("Vitc") 106 | self.setMinimumSize(960, 544) 107 | 108 | self.ip_box = QLineEdit() 109 | self.ip_box.setFixedWidth(200) 110 | self.ip_box.setPlaceholderText("IP") 111 | self.ip_box.setMaxLength(15) 112 | 113 | self.conn_btn = QPushButton("Connect") 114 | self.conn_btn.setFixedWidth(150) 115 | self.conn_btn.setFixedHeight(35) 116 | self.conn_btn.clicked.connect(self.connect) 117 | self.conn_btn.setStyleSheet("background-color: green; margin-left: 50px; margin-top: 10px;") 118 | 119 | layout = QVBoxLayout() 120 | layout.setAlignment(Qt.AlignCenter) 121 | layout.addWidget(self.ip_box) 122 | layout.addWidget(self.conn_btn) 123 | 124 | self.main_widget = QWidget() 125 | self.main_widget.setLayout(layout) 126 | self.frame_widget = FrameWidget() 127 | 128 | self.stack = QStackedWidget() 129 | self.stack.addWidget(self.main_widget) 130 | self.stack.addWidget(self.frame_widget) 131 | self.setCentralWidget(self.stack) 132 | 133 | def hide_main(self, d=False): 134 | self.stack.setCurrentIndex(1 if d else 0) 135 | 136 | def disable_main(self, d=True): 137 | self.conn_btn.setText("Wait..." if d else "Connect") 138 | self.conn_btn.setDisabled(d) 139 | self.ip_box.setDisabled(d) 140 | 141 | def connect(self): 142 | IP = self.ip_box.text() 143 | is_valid = bool(re.match(r'^(\d{1,3}\.){3}\d{1,3}$', IP)) 144 | 145 | if not is_valid: 146 | QMessageBox.critical(self, "Error", "Invalid IP") 147 | else: 148 | self.disable_main(True) 149 | 150 | self.connection_thread = ConnectionThread(IP, PORT) 151 | self.connection_thread.connectionEstablished.connect(self.on_connection_established) 152 | self.connection_thread.connectionUpdateFrame.connect(self.frame_widget.set_frame) 153 | self.connection_thread.connectionFailed.connect(self.on_connection_failed) 154 | self.connection_thread.start() 155 | 156 | def on_connection_established(self): 157 | self.hide_main(True) 158 | 159 | def on_connection_failed(self, error_message): 160 | self.hide_main(False) 161 | self.disable_main(False) 162 | QMessageBox.critical(self, "Error", error_message) 163 | 164 | def keyPressEvent(self, event): 165 | if event.key() == Qt.Key_F11: 166 | if self.isFullScreen(): 167 | self.showNormal() 168 | else: 169 | self.showFullScreen() 170 | 171 | 172 | if __name__ == '__main__': 173 | app = QApplication(sys.argv) 174 | 175 | # --------- Print FPS --------- 176 | def pfps(): 177 | global FPS 178 | 179 | while True: 180 | print('FPS: ' + str(FPS)) 181 | FPS = 0 182 | sleep(1) 183 | Thread(target=pfps).start() 184 | # --------- Print FPS --------- 185 | 186 | window = MainWindow() 187 | window.show() 188 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /client/requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5==5.15.9 2 | numpy 3 | opencv-python -------------------------------------------------------------------------------- /plugin/Makefile: -------------------------------------------------------------------------------- 1 | GREEN="\033[0;32m" 2 | NOCOLOR="\033[0m" 3 | 4 | TARGET = vitc 5 | OBJS = src/encoder.o src/main.o 6 | LIBS = -lSceIofilemgrForDriver_stub -lSceNetPsForDriver_stub \ 7 | -lSceModulemgrForKernel_stub -lSceDebugForDriver_stub \ 8 | -lSceThreadmgrForDriver_stub -lSceDisplayForDriver_stub \ 9 | -lSceSysclibForDriver_stub -lSceSysmemForDriver_stub \ 10 | -lSceIftuForDriver_stub 11 | 12 | PREFIX = arm-vita-eabi 13 | CC = $(PREFIX)-gcc 14 | CFLAGS += -Wl,-q -Wall -O2 -nostdlib -nostartfiles -mcpu=cortex-a9 -mthumb-interwork -Iinclude 15 | DEPS = $(OBJS:.o=.d) 16 | 17 | all: $(TARGET).skprx clean 18 | 19 | %.skprx: %.velf 20 | vita-make-fself -c $< $@ 21 | 22 | %.velf: %.elf 23 | vita-elf-create -e $(TARGET).yml $< $@ 24 | 25 | $(TARGET).elf: $(OBJS) 26 | $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) 27 | 28 | %.o: %.c 29 | $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ 30 | 31 | .PHONY: clean send 32 | 33 | clean: 34 | @rm -rf $(TARGET).velf $(TARGET).elf $(OBJS) $(DEPS) 35 | 36 | send: $(TARGET).skprx 37 | curl -T $(TARGET).skprx ftp://$(PSVITAIP):1337/ur0:/tai/$(TARGET).skprx 38 | @$(MAKE) clean 39 | @echo ${GREEN}"Sent."${NOCOLOR} -------------------------------------------------------------------------------- /plugin/src/encoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define WIDTH 480 8 | #define HEIGHT 272 9 | 10 | #define LOG_PATH "ux0:data/vitc/" 11 | #define LOG_FILE LOG_PATH "log.txt" 12 | 13 | #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) 14 | #define VIDEO_FRAME_SIZE_NV12(w, h) (((w) * (h) * 3) / 2) 15 | 16 | #define SCE_DISPLAY_PIXELFORMAT_BGRA5551 0x50000000 17 | #define MAX_FRAME_SIZE VIDEO_FRAME_SIZE_NV12(WIDTH, HEIGHT) 18 | 19 | #define START_FRAME_CODE "G^kGtPhoMR0&Xj2k0z7P7@^0iM*#AL*UgzfEab$Gjhk@nzNGHse3sKHPW6U6KPqdrADB5p8KaEn9$Lq#LMyuata8fatqOj6Gd" 20 | #define END_FRAME_CODE "@aejW9QqBnsR07eaUHy&MF7bEY#d2sG&Q7e6$bw^XWohJyH1ri8bdOUTpxJy2nu@q8e9HiFwZl*wanNFFPKS&DABtVpQjbBH2hd" 21 | 22 | 23 | static SceUID log_fd = -1; 24 | static SceUID frame_uid = -1; 25 | static char *frame_addr; 26 | 27 | 28 | static inline void LOG(char* msg) { 29 | if (log_fd > 0) { 30 | ksceIoWrite(log_fd, msg, strlen(msg)); 31 | ksceDebugPrintf(msg); 32 | } 33 | } 34 | 35 | 36 | static inline unsigned int display_to_iftu_pixelformat(unsigned int fmt) 37 | { 38 | switch (fmt) { 39 | case SCE_DISPLAY_PIXELFORMAT_A8B8G8R8: 40 | default: 41 | return SCE_IFTU_PIXELFORMAT_BGRX8888; 42 | case SCE_DISPLAY_PIXELFORMAT_BGRA5551: 43 | return SCE_IFTU_PIXELFORMAT_BGRA5551; 44 | } 45 | } 46 | 47 | 48 | static inline unsigned int display_pixelformat_bpp(unsigned int fmt) 49 | { 50 | switch (fmt) { 51 | case SCE_DISPLAY_PIXELFORMAT_A8B8G8R8: 52 | default: 53 | return 4; 54 | case SCE_DISPLAY_PIXELFORMAT_BGRA5551: 55 | return 2; 56 | } 57 | } 58 | 59 | 60 | static inline int frame_convert_to_nv12(const SceDisplayFrameBufInfo *fb_info, 61 | int dst_width, int dst_height) 62 | { 63 | uintptr_t dst_paddr; 64 | uintptr_t src_paddr = fb_info->paddr; 65 | unsigned int src_width = fb_info->framebuf.width; 66 | unsigned int src_width_aligned = ALIGN(src_width, 16); 67 | unsigned int src_pitch = fb_info->framebuf.pitch; 68 | unsigned int src_height = fb_info->framebuf.height; 69 | unsigned int src_pixelfmt = fb_info->framebuf.pixelformat; 70 | unsigned int src_pixelfmt_bpp = display_pixelformat_bpp(src_pixelfmt); 71 | 72 | static SceIftuCscParams RGB_to_YCbCr_JPEG_csc_params = { 73 | 0, 0x202, 0x3FF, 74 | 0, 0x3FF, 0, 75 | { 76 | { 0x99, 0x12C, 0x3A}, 77 | {0xFAA, 0xF57, 0x100}, 78 | {0x100, 0xF2A, 0xFD7} 79 | } 80 | }; 81 | 82 | ksceKernelGetPaddr(frame_addr, &dst_paddr); 83 | 84 | SceIftuConvParams params; 85 | memset(¶ms, 0, sizeof(params)); 86 | params.size = sizeof(params); 87 | params.unk04 = 1; 88 | params.csc_params1 = &RGB_to_YCbCr_JPEG_csc_params; 89 | params.csc_params2 = NULL; 90 | params.csc_control = 1; 91 | params.unk14 = 0; 92 | params.unk18 = 0; 93 | params.unk1C = 0; 94 | params.alpha = 0xFF; 95 | params.unk24 = 0; 96 | 97 | SceIftuPlaneState src; 98 | memset(&src, 0, sizeof(src)); 99 | src.fb.pixelformat = display_to_iftu_pixelformat(src_pixelfmt); 100 | src.fb.width = src_width_aligned; 101 | src.fb.height = src_height; 102 | src.fb.leftover_stride = (src_pitch - src_width_aligned) * src_pixelfmt_bpp; 103 | src.fb.leftover_align = 0; 104 | src.fb.paddr0 = src_paddr; 105 | src.unk20 = 0; 106 | src.src_w = (src_width * 0x10000) / dst_width; 107 | src.src_h = (src_height * 0x10000) / dst_height; 108 | src.dst_x = 0; 109 | src.dst_y = 0; 110 | src.src_x = 0; 111 | src.src_y = 0; 112 | 113 | SceIftuFrameBuf dst; 114 | memset(&dst, 0, sizeof(dst)); 115 | dst.pixelformat = SCE_IFTU_PIXELFORMAT_NV12; 116 | dst.width = dst_width; 117 | dst.height = dst_height; 118 | dst.leftover_stride = 0; 119 | dst.leftover_align = 0; 120 | dst.paddr0 = dst_paddr; 121 | dst.paddr1 = dst_paddr + dst_width * dst_height; 122 | 123 | return ksceIftuCsc(&dst, &src, ¶ms); 124 | } 125 | 126 | 127 | static inline int frame_init(unsigned int size) 128 | { 129 | int ret; 130 | 131 | SceKernelAllocMemBlockKernelOpt opt; 132 | SceKernelMemBlockType type; 133 | SceKernelAllocMemBlockKernelOpt *optp; 134 | 135 | type = 0x10208006; 136 | size = ALIGN(size, 4 * 1024); 137 | memset(&opt, 0, sizeof(opt)); 138 | opt.size = sizeof(opt); 139 | opt.attr = SCE_KERNEL_ALLOC_MEMBLOCK_ATTR_PHYCONT | 140 | SCE_KERNEL_ALLOC_MEMBLOCK_ATTR_HAS_ALIGNMENT; 141 | opt.alignment = 4 * 1024; 142 | optp = &opt; 143 | 144 | frame_uid = ksceKernelAllocMemBlock("frame_buffer", type, size, optp); 145 | if (frame_uid < 0) { 146 | LOG("Error allocating CSC dest memory\n"); 147 | return frame_uid; 148 | } 149 | 150 | ret = ksceKernelGetMemBlockBase(frame_uid, (void **)&frame_addr); 151 | if (ret < 0) { 152 | LOG("Error getting CSC desr memory addr\n"); 153 | ksceKernelFreeMemBlock(frame_uid); 154 | frame_uid = -1; 155 | return ret; 156 | } 157 | 158 | return 0; 159 | } 160 | 161 | 162 | static inline int frame_term() 163 | { 164 | if (frame_uid >= 0) { 165 | ksceKernelFreeMemBlock(frame_uid); 166 | frame_uid = -1; 167 | } 168 | 169 | return 0; 170 | } -------------------------------------------------------------------------------- /plugin/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "encoder.c" 7 | 8 | #define DEBUG 0 9 | #define PORT 54102 10 | #define BUFFER_SIZE 8160 11 | 12 | 13 | SceUID net_thread_uid = 0; 14 | SceUID addr_thread_uid = 0; 15 | SceNetSockaddrIn client; 16 | int socket = -1; 17 | unsigned int client_len = sizeof(client); 18 | 19 | 20 | int addr_thread(SceSize args, void *argp) 21 | { 22 | char tmp; 23 | int ret = -1; 24 | 25 | while(1) { 26 | ret = ksceNetRecvfrom(socket, &tmp, sizeof(tmp), 0, (SceNetSockaddr *)&client, &client_len); 27 | if (ret < 0) 28 | LOG("Failed to get initial data from client\n"); 29 | } 30 | } 31 | 32 | 33 | void send_frame() { 34 | int ret = -1; 35 | 36 | // Send start code 37 | ret = ksceNetSendto(socket, START_FRAME_CODE, sizeof(START_FRAME_CODE)-1, 0, (SceNetSockaddr *)&client, client_len); 38 | if (ret < 0) { 39 | LOG("Failed to send START_FRAME_CODE\n"); 40 | return; 41 | } 42 | 43 | // Process frame 44 | SceDisplayFrameBufInfo fb_info; 45 | int head = ksceDisplayGetPrimaryHead(); 46 | fb_info.size = sizeof(fb_info); 47 | fb_info.framebuf.pixelformat = SCE_DISPLAY_PIXELFORMAT_A8B8G8R8; 48 | ret = ksceDisplayGetProcFrameBufInternal(-1, head, 0, &fb_info); 49 | if (ret < 0 || fb_info.paddr == 0) 50 | ret = ksceDisplayGetProcFrameBufInternal(-1, head, 1, &fb_info); 51 | if (ret < 0) 52 | return; 53 | 54 | frame_term(); 55 | ret = frame_init(MAX_FRAME_SIZE); 56 | if (ret < 0) { 57 | LOG("Error allocating the frame\n"); 58 | return; 59 | } 60 | 61 | ret = frame_convert_to_nv12(&fb_info, WIDTH, HEIGHT); 62 | if (ret < 0) { 63 | LOG("Error sending NV12 frame\n"); 64 | return; 65 | } 66 | 67 | // Send frame 68 | for (int i = 0; i < MAX_FRAME_SIZE / BUFFER_SIZE; i++) { 69 | char* chunk_frame = frame_addr + (i * BUFFER_SIZE); 70 | 71 | ret = ksceNetSendto(socket, chunk_frame, BUFFER_SIZE, 0, (SceNetSockaddr *)&client, client_len); 72 | if (ret < 0) { 73 | LOG("Failed to send frame\n"); 74 | return; 75 | } 76 | 77 | ksceKernelDelayThread(1000); 78 | } 79 | 80 | // Send end code 81 | ret = ksceNetSendto(socket, END_FRAME_CODE, sizeof(END_FRAME_CODE)-1, 0, (SceNetSockaddr *)&client, client_len); 82 | if (ret < 0) { 83 | LOG("Failed to send END_FRAME_CODE\n"); 84 | return; 85 | } 86 | } 87 | 88 | 89 | int net_thread(SceSize args, void *argp) 90 | { 91 | int ret = -1; 92 | socket = ksceNetSocket("vitc_socket", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM, 0); 93 | if (socket < 0) { 94 | LOG("Failed to create socket\n"); 95 | return SCE_KERNEL_START_SUCCESS; 96 | } 97 | 98 | SceNetSockaddrIn server; 99 | server.sin_family = SCE_NET_AF_INET; 100 | server.sin_port = ksceNetHtons(PORT); 101 | server.sin_addr.s_addr = SCE_NET_INADDR_ANY; 102 | 103 | LOG("Trying to bind the socket...\n"); 104 | 105 | while(ret < 0) { 106 | ksceKernelDelayThread(3 * 1000 * 1000); 107 | 108 | ret = ksceNetBind(socket, (SceNetSockaddr *)&server, sizeof(server)); 109 | } 110 | 111 | LOG("Binded Successfully\n"); 112 | 113 | addr_thread_uid = ksceKernelCreateThread("addr_thread", addr_thread, 0x40, 0x10, 0, 0, 0); 114 | if (addr_thread_uid < 0) { 115 | LOG("Failed to create addr_thread\n"); 116 | return SCE_KERNEL_START_SUCCESS; 117 | } 118 | 119 | ksceKernelStartThread(addr_thread_uid, 0, NULL); 120 | 121 | while(1) { 122 | if (client.sin_addr.s_addr != 0) 123 | { 124 | ksceDisplayWaitVblankStart(); 125 | 126 | send_frame(); 127 | } 128 | else { 129 | ksceKernelDelayThread(100 * 1000); 130 | } 131 | } 132 | 133 | return SCE_KERNEL_START_SUCCESS; 134 | } 135 | 136 | 137 | void _start() __attribute__ ((weak, alias("module_start"))); 138 | int module_start(SceSize args, void *argp) 139 | { 140 | 141 | #if DEBUG == 1 142 | ksceIoMkdir(LOG_PATH, 6); 143 | log_fd = ksceIoOpen(LOG_FILE, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 6); 144 | if (log_fd < 0) { 145 | ksceIoWrite(log_fd, "Failed to Setup Debug", 22); 146 | return SCE_KERNEL_START_SUCCESS; 147 | } 148 | LOG("Vitc\n\n"); 149 | #endif 150 | 151 | 152 | net_thread_uid = ksceKernelCreateThread("net_thread", net_thread, 0x40, 0x1000, 0, 0, 0); 153 | if (net_thread_uid < 0) { 154 | LOG("Failed to create net_thread\n"); 155 | return SCE_KERNEL_START_SUCCESS; 156 | } 157 | 158 | ksceKernelStartThread(net_thread_uid, 0, NULL); 159 | 160 | return SCE_KERNEL_START_SUCCESS; 161 | } 162 | -------------------------------------------------------------------------------- /plugin/vitc.yml: -------------------------------------------------------------------------------- 1 | vitc: 2 | attributes: 0x7 3 | version: 4 | major: 0x1 5 | minor: 0x0 6 | main: 7 | start: module_start --------------------------------------------------------------------------------