PS3 PS3 net servers

When you receive a netiso_cmd (16 bytes) from the PS3, you first check the opcode.

For NETISO_CMD_OPEN_DIR (0x122A) you call this function with cmd (16 bytes) using the struct below:
ret = process_open_dir_cmd(index, (netiso_open_dir_cmd *)&cmd);

Code:
typedef struct _netiso_open_dir_cmd
{
   uint16_t opcode;
   uint16_t dp_len; // dirpath length
   uint8_t pad[12];
} __attribute__((packed)) netiso_open_dir_cmd;

The dp_len tells the amount of bytes to read after the command.
ret = recv(s, (void *)dirpath, dp_len, 0);

The received path is translated to the local path. e.g. if your shared folder is /shared

if dp_len = 7 and receive "/PS3ISO", the path is translated to /shared/PS3ISO

Then send a response of 0 or -1 to the PS3 in this struct:
Code:
typedef struct _netiso_open_dir_result
{
    int32_t open_result; // 0 success, -1 error
} __attribute__((packed)) netiso_open_dir_result;

The directory entries are returned as response of the netiso_cmd NETISO_CMD_READ_DIR (0x1232)
ret = process_read_dir_cmd(index, (netiso_read_dir_entry_cmd *)&cmd);
 
When you receive a netiso_cmd (16 bytes) from the PS3, you first check the opcode.

For NETISO_CMD_OPEN_DIR (0x122A) you call this function with cmd (16 bytes) using the struct below:
ret = process_open_dir_cmd(index, (netiso_open_dir_cmd *)&cmd);

Code:
typedef struct _netiso_open_dir_cmd
{
   uint16_t opcode;
   uint16_t dp_len; // dirpath length
   uint8_t pad[12];
} __attribute__((packed)) netiso_open_dir_cmd;

The dp_len tells the amount of bytes to read after the command.
ret = recv(s, (void *)dirpath, dp_len, 0);

The received path is translated to the local path. e.g. if your shared folder is /shared

if dp_len = 7 and receive "/PS3ISO", the path is translated to /shared/PS3ISO

Then send a response of 0 or -1 to the PS3 in this struct:
Code:
typedef struct _netiso_open_dir_result
{
    int32_t open_result; // 0 success, -1 error
} __attribute__((packed)) netiso_open_dir_result;

The directory entries are returned as response of the netiso_cmd NETISO_CMD_READ_DIR (0x1232)
ret = process_read_dir_cmd(index, (netiso_read_dir_entry_cmd *)&cmd);
Do you know what is inside the data of the OpenDirCommandResult (the byte array) returned to PS3? I tried to encapsulate the content inside a java class (java has no structs), but when this object is serialized, it becomes a very big byte array.

The class:
Code:
public class OpenDirCommandResult implements Serializable {
    private static final long serialVersionUID = 1L;
    private int openResult;

    public OpenDirCommandResult(int openResult) {
        this.openResult = openResult;
    }

    public int getOpenResult() {
        return this.openResult;
    }
}

And after serialize it to a byte array, it becomes a byte array with this inside (values are decimal):
[-84,-19,0,5,115,114,0,41,99,111,109,46,106,104,111,110,106,117,46,112,115,51,110,101,116,115,114,118,46,79,112,101,110,68,105,114,67,111,109,109,97,110,100,82,101,115,117,108,116,0,0,0,0,0,0,0,1,2,0,1,73,0,10,111,112,101,110,82,101,115,117,108,116,120,112,0,0,0,0]

Edit: Found a solution. I'll not be able to serialze objects. So, the easy way is to create the byte arrays manually, with fixed position for each struct property. I'll try to find another way than this... As "netiso_open_dir_result" has a int, it's easy to create a byte[] with 4 position filled with the result inside...
 
Last edited:
Edit: Found a solution. I'll not be able to serialze objects. So, the easy way is to create the byte arrays manually, with fixed position for each struct property. I'll try to find another way than this... As "netiso_open_dir_result" has a int, it's easy to create a byte[] with 4 position filled with the result inside...

yes, that was my concern about Java (how to build the byte structs to match the protocol), so that's why I suggested the wrapper idea.
Those functions are implemented and native in C, so my idea was that Java called some basic C function to build the byte array based on the data packet the Java app received.

Anyways, object serialization is not going to generate the same results as a "struct" so yes, I think your only way to do it purely in Java is to build the byte array yourself. Just try to keep track of the offsets to write the bytes, and the length of that field.
Also, keep an eye on the endianess of the integer stuff, because PS3 is big-endian and you might need to swap everything.
 
yes, that was my concern about Java (how to build the byte structs to match the protocol), so that's why I suggested the wrapper idea.
Those functions are implemented and native in C, so my idea was that Java called some basic C function to build the byte array based on the data packet the Java app received.

Anyways, object serialization is not going to generate the same results as a "struct" so yes, I think your only way to do it purely in Java is to build the byte array yourself. Just try to keep track of the offsets to write the bytes, and the length of that field.
Also, keep an eye on the endianess of the integer stuff, because PS3 is big-endian and you might need to swap everything.
At least, Java is also BE :D

The OpenDirCommand is ready, next is the ReadDirCommand :D
 
Do you know what is inside the data of the OpenDirCommandResult (the byte array) returned to PS3? I tried to encapsulate the content inside a java class (java has no structs), but when this object is serialized, it becomes a very big byte array.

The class:
Code:
public class OpenDirCommandResult implements Serializable {
    private static final long serialVersionUID = 1L;
    private int openResult;

    public OpenDirCommandResult(int openResult) {
        this.openResult = openResult;
    }

    public int getOpenResult() {
        return this.openResult;
    }
}

And after serialize it to a byte array, it becomes a byte array with this inside:
[-84,-19,0,5,115,114,0,41,99,111,109,46,106,104,111,110,106,117,46,112,115,51,110,101,116,115,114,118,46,79,112,101,110,68,105,114,67,111,109,109,97,110,100,82,101,115,117,108,116,0,0,0,0,0,0,0,1,2,0,1,73,0,10,111,112,101,110,82,101,115,117,108,116,120,112,0,0,0,0]

Edit: Found a solution. I'll not be able to serialze objects. So, the easy way is to create the byte arrays manually, with fixed position for each struct property. I'll try to find another way than this... As "netiso_open_dir_result" has a int, it's easy to create a byte[] with 4 position filled with the result inside...
I suppose the nearest "equivalent" to C structs in java is a class with only public members & no accessors.
If you introduce accessors for private members, you move away from the struct concept.

Also iirc Java introduced a new Record class at some point to help with similar use case scenarios.
 
I suppose the nearest "equivalent" to C structs in java is a class with only public members & no accessors.
If you introduce accessors for private members, you move away from the struct concept.

Also iirc Java introduced a new Record class at some point to help with similar use case scenarios.
Yeah, I'll try that also, but I think I can write my own readObject/writeObject methods to properly serialize the info. If these alternatives didn't work, I'll try to create a method inside the "ICommandResult" interface (that I'll also create): "toByteArray" that properly converts the object to byte array.

About the Record class, I'll check that also (before other things)
 
Yeah, I'll try that also, but I think I can write my own readObject/writeObject methods to properly serialize the info. If these didn't work, I'll try to create a method inside the "ICommandResult" interface (that I'll also create): "toByteArray" that properly converts the object to byte array.
You can check with a class with only public members, and if that doesn't help, keeping in mind that I have not worked on Java for over a decade (time flies lol), things will have changed so I may not be the best person to ask, I don't think using standard java (or json) serialization can work unless you add appropriate serialization code on the C side.
So if you wish to leave the C code alone, you don't have a choice, you need to implement the format expected/created by the C code in your custom java serialization code, at least that used to be the recommended way to deal with this situation as far as I remember.

Having said that, if the C code communicating with your java code deserves to be improved for better overall interoperability, changes to the C code should be considered, even if that means updating a number of client homebrews.
You would obviously want to avoid that if you can but if such changes were in the interest of ps3netsrv users, it can be done, even deank may update multiman (the only client not open sourced) if he considers the improvements worthy.
 
Last edited:
You can check with a class with only public members, and if that doesn't help, keeping in mind that I have not worked on Java for over a decade (time flies lol), things will have changed so I may not be the best person to ask, I don't think using standard java (or json) serialization can work unless you add appropriate serialization code on the C side.
So if you wish to leave the C code alone, you don't have a choice, you need to implement the format expected/created by the C code in your custom java serialization code, at least that used to be the recommended way to deal with this situation as far as I remember.

Having said that, if the C code communicating with your java code deserves to be improved for better overall interoperability, changes to the C code should be considered, even if that means updating a number of client homebrews.
You would obviously want to avoid that if you can but if such changes were in the interest of ps3netsrv users, it can be done.
I'll write the serialization code properly. I tried with the class with only public members and no constructors/methods, but even in this situation, it's creating a big byte array. About the Record, it's a new thing of Java 14, but I'm using Android SDK 30 (Java 11). About write my own methods, it's a lot simple, but of course, if something change on some properties inside the result structs, these methods will also need to be changed, but it's not a big problem as ps3netsrv is a lot stable and I don't think the protocol will change.
 
I'll write the serialization code properly. I tried with the class with only public members and no constructors/methods, but even in this situation, it's creating a big byte array. About the Record, it's a new thing of Java 14, but I'm using Android SDK 30 (Java 11). About write my own methods, it's a lot simple, but of course, if something change on some properties inside the result structs, the app will also need to be changed, but it's not a big problem as ps3netsrv is a lot stable and I don't think the protocol will change.
I think overriding the default serialization/deserialization functions is the right approach in this case.

The "official" protocol is extremely unlikely to change unless someone worked on a "2.0" kind of project, simply because changing the protocol has to be worth the trouble. Think of all the troubles that the various samba protocol changes have produced for both Linux & Windows samba clients users. To this day, I experience smbv1 vs smbv2 issues from time to time while samba itself moved on to v3... Lol. It goes without saying that ps3netsrv doesn't have the same user base or variety of clients but overall the issues would be exactly the same.
 
Last edited:
I almost got the ReadDirCommand working...

The "modifiedDate" attribute is being read wrong, and I still don't know why:
ps3netsrvAndroidReadDirCommand.png

It's getting a long value that represents the time the file was last modified, measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970). Is this what the PS3 expects for display the date?
 
Last edited:
I almost got the ReadDirCommand working...

The "modifiedDate" attribute is being read wrong, and I still don't know why:

It's getting a long value that represents the time the file was last modified, measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970). Is this what the PS3 expects for display the date?

Fix the date later. Focus on the core features.
 
Fix the date later. Focus on the core features.
I've created a private repo at github (as it is still in a very initial stage, I'll keep it private until it get more maturity). I gave you access to the repository. You can get the latest build on "actions" (I've created a gradle script to build it when a push happens).

Forgot to mention: there is an issue about file/folder read permission, so the SO is not letting me select any other folder than the data folder... I'll fix that later...
 
Last edited:
A typical connection starts with the command NETISO_CMD_OPEN_FILE. It calls the function process_open_cmd where a relative path of the ISO is received (e.g. /PS3ISO/game.iso). The function translates the relative path to a full path in the local file system. Then validate that the file exists, opens the file and receive a file handle, detects the sector size (2048 by default for BD and DVD, 2352 for CDs). If the ISO is multi-part, all the parts are opened with their respective file handles. A response is returned to the client telling the total size of the ISO (or sum of sizes of the multi-parts). If the file does not exists, -1 is returned.
This is the explanation I was looking for and needing right now :D
 
Aldo is right, you should probably start with the core features & expand into additional stuff only when the server core is running flawlessly.

It might be possible to compile & run ps3netsrv on Termux actually but in any case a java implementation would be better for Android & a worthy addition to the ps3netsrv project.

I just realise now it would be nice for me to have a ps3netsrv java app on my new Android TV box, your project comes at a perfect time... Lol
 
Aldo is right, you should probably start with the core features & expand into additional stuff only when the server core is running flawlessly.

It might be possible to compile & run ps3netsrv on Termux actually but in any case a java implementation would be better for Android & a worthy addition to the ps3netsrv project.

I just realise now it would be nice for me to have a ps3netsrv java app on my new Android TV box, your project comes at a perfect time... Lol
The Android TV boxes are the real reason to this ;). I even lowered the Android SDK level to include the old boxes with Android 4.4 to the compatibility list (a lot of people have old Android boxes, and the ps3netsrv will give a purpose to them)
As soon as I end the core features I'll let the GitHub repo public.
 
About this line:

Code:
if(client->ro_file->open(filepath, O_RDONLY) < 0)
https://github.com/aldostools/webMA...c93ce0/_Projects_/ps3netsrv/src/main.cpp#L511

Is "ro_file" the same as "read only file"?

the struct member "ro_file" is a pointer of type:
AbstractFile *ro_file;

from what I see, AbstractFile is an abstract class, and is implemented by VIsoFile.
So "ro_file" is an instance of any AbstractFile implementation. You'll have to implement those classes and mirror/port the methods.
 
Alternatively you may consider using a JNI (Java Native Interface) wrapper around existing C/C++ implementations compiled as a shared library. It could be quicker to do than a java rewrite, it would ensure specs compatibility & depending on the situation, you may even be able to avoid implementing custom serialization as a bonus. It could also make future updates/fixes in the C/C++ code easier to deploy project wide.

Taken to the extreme (you should not need that here), JNI usage can even support the introduction of cpp coding into java in the same way as we can use assembly directly in C code.
Check this app out for instance:
https://github.com/bytedeco/javacpp
 
Last edited:
About this line:

Code:
if(client->ro_file->open(filepath, O_RDONLY) < 0)
https://github.com/aldostools/webMA...c93ce0/_Projects_/ps3netsrv/src/main.cpp#L511

Is "ro_file" the same as "read only file"?
Yes, it is the same.

AbstractFile class is a wrapper of the file functions.

You could create your own wrapper or use the functions directly.

The wrapper probably was created to make a standard code, and handle easier the port to different environments (Windows, linux, etc.)
 
Last edited:
Back
Top