How webMAN MOD Implements the NETISO Protocol for Streaming PS3 Games Over Network

webMAN MOD streams PS3, PS1, DVD, and Blu-ray ISOs over a LAN using a custom binary NETISO protocol that pairs a PS3 client with the PC-side ps3netsrv daemon on TCP port 38008, intercepting SCSI commands and forwarding them as network packets to simulate a locally attached disc drive.

The aldostools/webman-mod repository contains the complete implementation of this protocol, enabling users to mount and play games stored on a remote PC without copying them to the console's local storage. The architecture splits functionality between a lightweight NETISO client embedded in the webMAN MOD plugin and the ps3netsrv server application that handles file-system operations on the host machine.

NETISO Protocol Architecture

The NETISO system relies on two cooperating components that communicate via raw binary packets over TCP port 38008. When a user selects a network game in the webMAN MOD interface, the plugin constructs a net:// URL (for example, net://192.168.1.10/ISO/Game.iso) and initiates a mount sequence.

The PS3-side NETISO client, defined in include/mount/netclient.h, opens a persistent TCP socket to the server and translates Cobra driver SCSI requests into protocol commands. The PC-side NETISO server, implemented in _Projects_/ps3netsrv/src/main.cpp, listens for these commands, performs the corresponding file I/O, and streams raw bytes back to the client. From the PS3's perspective, the data appears to originate from a local BD-ROM drive.

Protocol Command Reference

The NETISO protocol defines a fixed set of opcodes in cobra/netiso.h for file and directory operations. All multi-byte integers use big-endian encoding, with the PS3 client automatically converting host byte order using inline helpers BE16, BE32, and BE64.

Opcode Command Client Sends Server Returns
NETISO_CMD_OPEN_FILE (0x1224) Open a read-only file _netiso_open_cmd with path length _netiso_open_result containing file size and mtime
NETISO_CMD_READ_FILE Standard read request _netiso_read_file_cmd with offset and byte count _netiso_read_file_result followed by raw data
NETISO_CMD_READ_FILE_CRITICAL Fast read without error recovery _netiso_read_file_critical_cmd Raw data (client aborts on failure)
NETISO_CMD_READ_CD_2048_CRITICAL Read 2048-byte sectors for DVD/BD _netiso_read_cd_2048_critical_cmd Raw sector data
NETISO_CMD_STAT_FILE Retrieve file metadata _netiso_stat_cmd _netiso_stat_result with size and timestamps
NETISO_CMD_OPEN_DIR Open directory for listing _netiso_open_dir_cmd _netiso_open_dir_result
NETISO_CMD_READ_DIR Fetch directory entries _netiso_read_dir_cmd _netiso_read_dir_result with entry list
NETISO_CMD_CUSTOM_0 (0x2412) Extension point for future use

Client-Side Implementation in webMAN MOD

The PS3 implementation integrates directly with the Cobra driver to intercept disc access requests.

Mounting Network ISOs

When a user triggers a mount via the webMAN MOD UI, the code in _Projects_/netiso/main.c and netiso.c creates a netiso_args structure containing the server IP, remote path, and emulation mode. The mount_iso() function parses the net:// URL and invokes the NETISO client API, which registers itself with Cobra using SYS_MODULE_INFO(NETISO, …). Once registered, all subsequent SCSI read commands route through the NETISO client rather than the physical drive.

Building Binary Command Packets

The client constructs packets by populating C structs and converting fields to big-endian. Below is a simplified version of the file-opening logic found in the client implementation:

// Simplified from netclient.c
int netiso_open(const char *server, const char *path)
{
    int s = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(NETISO_PORT) };
    inet_pton(AF_INET, server, &addr.sin_addr);
    connect(s, (struct sockaddr *)&addr, sizeof(addr));

    // Build open command with proper byte order
    netiso_open_cmd cmd = { 
        .opcode = BE16(NETISO_CMD_OPEN_FILE), 
        .fp_len = BE16(strlen(path)) 
    };
    send(s, &cmd, sizeof(cmd), 0);
    send(s, path, strlen(path), 0);

    netiso_open_result res;
    recv(s, &res, sizeof(res), 0);
    res.file_size = (s64)BE64(res.file_size);
    return s;   // Socket handle for subsequent reads
}

For reading data, the client sends a _netiso_read_file_cmd structure specifying the byte offset and length, then receives the payload in two stages: first the result header containing the actual bytes read, then the raw ISO data.

// Read request construction
netiso_read_file_cmd rcmd = {
    .opcode     = BE16(NETISO_CMD_READ_FILE),
    .pad        = 0,
    .num_bytes  = BE32(0x10000),          // 64 KB chunks
    .offset     = BE64(file_offset)
};
send(s, &rcmd, sizeof(rcmd), 0);

netiso_read_file_result rres;
recv(s, &rres, sizeof(rres), 0);
int bytes = BE32(rres.bytes_read);
recv(s, buffer, bytes, 0);   // Raw ISO sector data

Server-Side Implementation (ps3netsrv)

The PC-side daemon ps3netsrv handles the actual file-system operations. The source in _Projects_/ps3netsrv/src/main.cpp implements a blocking TCP server that maintains per-connection state, including open file pointers.

Command Dispatch Loop

The server enters a loop receiving 16-bit opcodes, switching on the command type, and executing the corresponding handler. For NETISO_CMD_OPEN_FILE, it reconstructs the filename, opens the file in binary mode, and returns the size:

// Excerpt from _Projects_/ps3netsrv/src/main.cpp
case NETISO_CMD_OPEN_FILE:
{
    netiso_open_cmd cmd;
    recv(sock, &cmd, sizeof(cmd), 0);
    cmd.opcode = BE16(cmd.opcode);
    
    char filename[0x420];
    recv(sock, filename, BE16(cmd.fp_len), 0);
    
    FILE *fp = fopen(filename, "rb");
    netiso_open_result res = { .file_size = -1, .mtime = 0 };
    if (fp) {
        fseek(fp, 0, SEEK_END);
        res.file_size = ftell(fp);
        rewind(fp);
    }
    res.file_size = BE64(res.file_size);
    send(sock, &res, sizeof(res), 0);
    // File pointer stored in connection state for reads
}
break;

Streaming File Data

For read operations, the server seeks to the requested offset, allocates a temporary buffer, and transmits the data immediately:

case NETISO_CMD_READ_FILE:
{
    netiso_read_file_cmd cmd;
    recv(sock, &cmd, sizeof(cmd), 0);
    uint64_t off = BE64(cmd.offset);
    uint32_t nbytes = BE32(cmd.num_bytes);

    fseek(state->fp, off, SEEK_SET);
    char *buf = new char[nbytes];
    size_t got = fread(buf, 1, nbytes, state->fp);

    netiso_read_file_result res = { .bytes_read = BE32((uint32_t)got) };
    send(sock, &res, sizeof(res), 0);
    send(sock, buf, got, 0);
    delete[] buf;
}
break;

This zero-copy-like approach ensures minimal latency for streaming high-bandwidth Blu-ray ISOs over gigabit Ethernet.

Summary

  • webMAN MOD implements the NETISO protocol to stream disc images over TCP port 38008, enabling network gameplay without local storage.
  • The client in include/mount/netclient.h converts Cobra SCSI requests into binary commands using big-endian byte order helpers (BE16, BE32, BE64).
  • The server in _Projects_/ps3netsrv/src/main.cpp dispatches opcodes such as NETISO_CMD_OPEN_FILE and NETISO_CMD_READ_FILE to serve file data in 64 KB chunks or 2048-byte sectors.
  • Integration occurs through net:// URL parsing in _Projects_/netiso/main.c, which registers the NETISO module with the Cobra driver to intercept disc reads.

Frequently Asked Questions

What network port does the NETISO protocol use?

The NETISO protocol uses TCP port 38008 for all client-server communication. The ps3netsrv daemon binds to this port by default, and the webMAN MOD client connects to it when mounting net:// URLs.

Can NETISO stream folder-based games or only ISO files?

NETISO supports both ISO files and folder-based games. While ISOs use NETISO_CMD_READ_FILE for byte-offset reads, folder-based content (JBOD format) uses NETISO_CMD_OPEN_DIR and NETISO_CMD_READ_DIR to browse directory structures, with individual files accessed via the standard file commands.

How does webMAN MOD handle byte order in NETISO packets?

All NETISO packets use big-endian encoding to match the PS3's PowerPC architecture. The client and server use inline conversion macros BE16, BE32, and BE64 defined in cobra/netiso.h to swap byte order when packing or unpacking command structures on little-endian PC hosts.

Is the NETISO server compatible with other PS3 homebrew besides webMAN MOD?

The ps3netsrv implementation is specific to the NETISO protocol used by webMAN MOD and Cobra firmware. While the protocol is open-source, compatibility with other tools depends on whether they implement the same command set found in include/mount/netclient.h and _Projects_/ps3netsrv/include/netiso.h.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →