[Android Software Development] JohnMonsour:
Plausible and an achievable concept... is it worth it? Lol?

This got me thinking.

I dont know why anyone would want to do this now a days but it is very possible.

Back in 2007 remotejoy came out on the psp-1001 Phat.

Basically the concept is simple you would put a couple of .prx files on the memory stick pro duo in the root directory in a folder named plugins. And set up a windows drivers and a small application I think. I'm not 100% about that but seems logical.

Then interface the psp with the pc via USB but before interfacing, you would have to go into the vsh menu which you would hit a button combo and a text overlay would appear over the xmb in the top left hand corner. You could then select what plugin you wanted to load. Then you would load up the prx's and plug in your controller and USB type B from the psp to the pc. It would detect it though libusb and then you simply execute remotejoy.exe "I think" don't quote me on any of this. It was a long time ago....

Then you would literally be able to play your game on your monitor or on a flat screen like me. Via dvi output from an ant 1600xt graphics card in crossfire. And my 10000 pound Sony professional 42 inch $4000 plasma..... and then thru remotejoy gui you could go to controllers and basically use any analog controller that there were drivers for...... Man things have really changed in 13 years!!!!.... Jeez.

These plugins could add all kinds of extra features. Fps counter, background music player, an ir yeah infrared.... lol irmanager a badass file explorer with every feature you could ever imagine, well there was an irshell .prx, and a psx one called pops before Sony ever even released it.....

Sound familiar? Should, first there was xposed framework, now we have magisk plugins with support for xposed modules....

With that being said this is very possible and a feasible feat. (Not that anyone should take it on).....

It was coded under unix. So same kernel we use on androids..... It could be forked/updated/shimed/referenced/converted/reverse engineer to remotejoy magisk plugin but it's old code.

I think the last time it was updated was in 09 when I graduated high school.... so someone way more talented then me could possibly modify and retrofit or port to android using adb and USB debugging on a rooted android.

But this is not my area of expertise ports aren't my bag, man.

It would be a huge undertaking.

For controller support it would use socks. TCP specifically.

I didn't dig into it too much because well everyone now a days has smart share or screen mirroring and bluetooth xbox one s controllers and 4k TV's......

Just wanted to say it it a very plausible and achievable project.

Sorry to dig up an old thread. I have the original source code somewhere.

Here's an excerpt for controller support and mapping via universal serial ports it's in raw.

/*

* PSPLINK

* -----------------------------------------------------------------------

* Licensed under the BSD license, see LICENSE in PSPLINK root for details.

*

* remotejoy.c - PSPLINK PC remote joystick handler (SDL Version)

*

* Copyright (c) 2006 James F <tyranid@gmail.com>

*

* $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/tools/remotejoy/pcsdl/remotejoy.c $

* $Id: remotejoy.c 2187 2007-02-20 19:28:00Z tyranid $

*/

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/select.h>

#include <sys/socket.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <netinet/in.h>

#include <netinet/tcp.h>

#include <netdb.h>

#include <limits.h>

#include <errno.h>

#include <ctype.h>

#include <signal.h>

#include <string.h>

#include <SDL.h>

#include <SDL_thread.h>

#include "../remotejoy.h"

#define DEFAULT_PORT 10004

#define DEFAULT_IP "localhost"

#define MAX_AXES_NUM 32767

#define DIGITAL_TOL 10000

#define PSP_SCREEN_W 480

#define PSP_SCREEN_H 272

#define EVENT_ENABLE_SCREEN 1

#define EVENT_RENDER_FRAME_1 2

#define EVENT_RENDER_FRAME_2 3

#define EVENT_DISABLE_SCREEN 4

#ifndef SOL_TCP

#define SOL_TCP IPPROTO_TCP

#endif

#if defined BUILD_BIGENDIAN || defined _BIG_ENDIAN

uint16_t swap16(uint16_t i)

{

uint8_t *p = (uint8_t *) &i;

uint16_t ret;

ret = (p[1] << 8) | p[0];

return ret;

}

uint32_t swap32(uint32_t i)

{

uint8_t *p = (uint8_t *) &i;

uint32_t ret;

ret = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];

return ret;

}

uint64_t swap64(uint64_t i)

{

uint8_t *p = (uint8_t *) &i;

uint64_t ret;

ret = (uint64_t) p[0] | ((uint64_t) p[1] << 8) | ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24)

| ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) | ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56);

return ret;

}

#define LE16(x) swap16(x)

#define LE32(x) swap32(x)

#define LE64(x) swap64(x)

#else

#define LE16(x) (x)

#define LE32(x) (x)

#define LE64(x) (x)

#endif

enum PspCtrlButtons

{

/** Select button. */

PSP_CTRL_SELECT = 0x000001,

/** Start button. */

PSP_CTRL_START = 0x000008,

/** Up D-Pad button. */

PSP_CTRL_UP = 0x000010,

/** Right D-Pad button. */

PSP_CTRL_RIGHT = 0x000020,

/** Down D-Pad button. */

PSP_CTRL_DOWN	= 0x000040,

/** Left D-Pad button. */

PSP_CTRL_LEFT	= 0x000080,

/** Left trigger. */

PSP_CTRL_LTRIGGER = 0x000100,

/** Right trigger. */

PSP_CTRL_RTRIGGER = 0x000200,

/** Triangle button. */

PSP_CTRL_TRIANGLE = 0x001000,

/** Circle button. */

PSP_CTRL_CIRCLE = 0x002000,

/** Cross button. */

PSP_CTRL_CROSS = 0x004000,

/** Square button. */

PSP_CTRL_SQUARE = 0x008000,

/** Home button. */

PSP_CTRL_HOME = 0x010000,

/** Music Note button. */

PSP_CTRL_NOTE = 0x800000,

/** Screen button. */

PSP_CTRL_SCREEN = 0x400000,

/** Volume up button. */

PSP_CTRL_VOLUP = 0x100000,

/** Volume down button. */

PSP_CTRL_VOLDOWN = 0x200000,

};

enum PspButtons

{

PSP_BUTTON_CROSS = 0,

PSP_BUTTON_CIRCLE = 1,

PSP_BUTTON_TRIANGLE = 2,

PSP_BUTTON_SQUARE = 3,

PSP_BUTTON_LTRIGGER = 4,

PSP_BUTTON_RTRIGGER = 5,

PSP_BUTTON_START = 6,

PSP_BUTTON_SELECT = 7,

PSP_BUTTON_UP = 8,

PSP_BUTTON_DOWN = 9,

PSP_BUTTON_LEFT = 10,

PSP_BUTTON_RIGHT = 11,

PSP_BUTTON_HOME = 12,

PSP_BUTTON_NOTE = 13,

PSP_BUTTON_SCREEN = 14,

PSP_BUTTON_VOLUP = 15,

PSP_BUTTON_VOLDOWN = 16,

PSP_BUTTON_MAX = 17

};

unsigned int g_bitmap[PSP_BUTTON_MAX] = {

PSP_CTRL_CROSS, PSP_CTRL_CIRCLE, PSP_CTRL_TRIANGLE, PSP_CTRL_SQUARE,

PSP_CTRL_LTRIGGER, PSP_CTRL_RTRIGGER, PSP_CTRL_START, PSP_CTRL_SELECT,

PSP_CTRL_UP, PSP_CTRL_DOWN, PSP_CTRL_LEFT, PSP_CTRL_RIGHT, PSP_CTRL_HOME,

PSP_CTRL_NOTE, PSP_CTRL_SCREEN, PSP_CTRL_VOLUP, PSP_CTRL_VOLDOWN

};

const char *map_names[PSP_BUTTON_MAX] = {

"cross", "circle", "triangle", "square",

"ltrig", "rtrig", "start", "select",

"up", "down", "left", "right", "home",

"note", "screen", "volup", "voldown"

};

/* Maps the buttons on the joystick to the buttons on the PSP controller */

unsigned int *g_buttmap = NULL;

struct Args

{

const char *ip;

unsigned short port;

const char *dev;

const char *mapfile;

int verbose;

int video;

int fullscreen;

int droprate;

int fullcolour;

int halfsize;

int showfps;

};

struct GlobalContext

{

struct Args args;

struct sockaddr_in serv;

char name[128];

unsigned int version;

unsigned char axes;

unsigned char buttons;

int exit;

int digital;

int analog;

int tol;

int scron;

};

struct GlobalContext g_context;

struct ScreenBuffer

{

unsigned char buf[PSP_SCREEN_W * PSP_SCREEN_H * 4];

struct JoyScrHeader head;

/* Mutex? */

};

static struct ScreenBuffer g_buffers[2];

void init_font(void);

void print_text(SDL_Surface *screen, int x, int y, const char *fmt, ...);

/* Should have a mutex on each screen */

#define VERBOSE (g_context.args.verbose)

int fixed_write(int s, const void *buf, int len)

{

int written = 0;

while(written < len)

{

int ret;

ret = write(s, buf+written, len-written);

if(ret < 0)

{

if(errno != EINTR)

{

perror("write");

written = -1;

break;

}

}

else

{

written += ret;

}

}

return written;

}

int parse_args(int argc, char **argv, struct Args *args)

{

memset(args, 0, sizeof(*args));

args->ip = DEFAULT_IP;

args->port = DEFAULT_PORT;

while(1)

{

int ch;

int error = 0;

ch = getopt(argc, argv, "vsfchldp:i:m:r:");

if(ch < 0)

{

break;

}

switch(ch)

{

case 'p': args->port = atoi(optarg);

break;

case 'i': args->ip = optarg;

break;

case 'm': args->mapfile = optarg;

break;

case 'v': args->verbose = 1;

break;

case 'd': args->video = 1;

break;

case 'f': args->fullscreen = 1;

break;

case 'c': args->fullcolour = 1;

break;

case 'l': args->halfsize = 1;

break;

case 's': args->showfps = 1;

break;

case 'r': args->droprate = atoi(optarg);

if((args->droprate < 0) || (args->droprate > 59))

{

fprintf(stderr, "Invalid drop rate (0 <= r < 60)\n");

error = 1;

}

break;

case 'h':

default : error = 1;

break;

};

if(error)

{

return 0;

}

}

argc -= optind;

argv += optind;

return 1;

}

void print_help(void)

{

fprintf(stderr, "Remotejoy Help\n");

fprintf(stderr, "Usage: remotejoy [options]\n");

fprintf(stderr, "Options:\n");

fprintf(stderr, "-p port : Specify the port number\n");

fprintf(stderr, "-i ip : Specify the ip address (default %s)\n", DEFAULT_IP);

fprintf(stderr, "-m mapfile : Specify a file to map joystick buttons to the PSP\n");

fprintf(stderr, "-d : Auto enable display support\n");

fprintf(stderr, "-f : Full screen mode\n");

fprintf(stderr, "-r drop : Frame Skip, 0 (auto), 1 (1/2), 2 (1/3), 3(1/4) etc.\n");

fprintf(stderr, "-c : Full colour mode\n");

fprintf(stderr, "-l : Half size mode (both X and Y)\n");

fprintf(stderr, "-s : Show fps\n");

fprintf(stderr, "-v : Verbose mode\n");

}

int init_sockaddr(struct sockaddr_in *name, const char *ipaddr, unsigned short port)

{

struct hostent *hostinfo;

name->sin_family = AF_INET;

name->sin_port = htons(port);

hostinfo = gethostbyname(ipaddr);

if(hostinfo == NULL)

{

fprintf(stderr, "Unknown host %s\n", ipaddr);

return 0;

}

name->sin_addr = *(struct in_addr *) hostinfo->h_addr;

return 1;

}

int connect_to(const char *ipaddr, unsigned short port)

{

struct sockaddr_in name;

int sock = -1;

int flag = 1;

sock = socket(PF_INET, SOCK_STREAM, 0);

if(sock < 0)

{

perror("socket");

return -1;

}

if(!init_sockaddr(&name, ipaddr, port))

{

printf("Could not initialise socket address\n");

close(sock);

return -1;

}

if(connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0)

{

perror("connect");

close(sock);

return -1;

}

/* Disable NAGLE's algorithm to prevent the packets being joined */

setsockopt(sock, SOL_TCP, TCP_NODELAY, &flag, sizeof(int));

return sock;

}

int get_joyinfo(SDL_Joystick *stick)

{

const char *name;

name = SDL_JoystickName(0);

if(!name)

{

return 0;

}

strcpy(g_context.name, name);

g_context.axes = SDL_JoystickNumAxes(stick);

g_context.buttons = SDL_JoystickNumButtons(stick);

return 1;

}

void remove_wsp(char *buf)

{

int len = strlen(buf);

int i = 0;

while(isspace(buf))

{

i++;

}

if(i > 0)

{

len -= i;

memmove(buf, &buf, len + 1);

}

if(len <= 0)

{

return;

}

i = len-1;

while(isspace(buf))

{

buf[i--] = 0;

}

}

int build_map(const char *mapfile, int buttons)

{

int i;

FILE *fp;

g_context.analog = -1;

g_context.digital = -1;

g_context.tol = DIGITAL_TOL;

g_buttmap = (unsigned int *) malloc(buttons * sizeof(unsigned int));

if(g_buttmap == NULL)

{

return 0;

}

for(i = 0; i < buttons; i++)

{

/* Fill with mappings, repeat if more than 8 buttons */

g_buttmap = i % 8;

}

if(mapfile)

{

char buffer[512];

int line = 0;

fp = fopen(mapfile, "r");

if(fp == NULL)

{

fprintf(stderr, "Could not open mapfile %s\n", mapfile);

return 0;

}

while(fgets(buffer, sizeof(buffer), fp))

{

char *tok, *val;

int butt;

line++;

remove_wsp(buffer);

if((buffer[0] == '#') || (buffer[0] == 0)) /* Comment or empty line */

{

continue;

}

tok = strtok(buffer, ":");

val = strtok(NULL, "");

if((tok == NULL) || (val == NULL))

{

printf("Invalid mapping on line %d\n", line);

continue;

}

butt = atoi(val);

for(i = 0; i < PSP_BUTTON_MAX; i++)

{

if(strcasecmp(map_names, tok) == 0)

{

g_buttmap[butt] = i;

break;

}

}

if(i == PSP_BUTTON_MAX)

{

if(strcasecmp("analog", tok) == 0)

{

g_context.analog = butt;

}

else if(strcasecmp("digital", tok) == 0)

{

g_context.digital = butt;

}

else if(strcasecmp("tol", tok) == 0)

{

g_context.tol = atoi(val);

}

else

{

fprintf(stderr, "Unknown map type %s\n", tok);

}

}

}

fclose(fp);

}

return 1;

}

int send_event(int sock, int type, unsigned int value)

{

struct JoyEvent event;

if(sock < 0)

{

return 0;

}

/* Note, should swap endian */

event.magic = LE32(JOY_MAGIC);

event.type = LE32(type);

event.value = LE32(value);

if(fixed_write(sock, &event, sizeof(event)) != sizeof(event))

{

fprintf(stderr, "Could not write out data to socket\n");

return 0;

}

return 1;

}

void post_event(int no)

{

SDL_Event event;

event.type = SDL_USEREVENT;

event.user.code = no;

event.user.data1 = NULL;

event.user.data2 = NULL;

SDL_PushEvent(&event);

}

int flush_socket(int sock)

{

/* If we encounter some horrible error which means we are desynced

* then send a video off packet to remotejoy, wait around for a second sucking up

* any more data from the socket and then reenable */

return 0;

}

void update_fps(SDL_Surface *screen)

{

#define FRAME_VALUES 32

static Uint32 times[FRAME_VALUES];

static Uint32 lastticks = 0;

static int index = 0;

Uint32 ticks;

int i;

double fps;

ticks = SDL_GetTicks();

times[index] = ticks - lastticks;

index = (index + 1) % FRAME_VALUES;

lastticks = ticks;

fps = 0.0;

for(i = 0; i < FRAME_VALUES; i++)

{

fps += (double) times;

}

fps /= (double) FRAME_VALUES;

/* Fps is now average frame time */

fps = 1000.0 / fps;

/* Now frame frequency in Hz */

print_text(screen, 0, 0, "Fps: %.2f", fps);

}

int read_thread(void *p)

{

int err = 0;

int frame = 0;

fd_set saveset, readset;

int count;

int sock = *(int *) p;

struct JoyScrHeader head;

FD_ZERO(&saveset);

FD_SET(sock, &saveset);

while(!err)

{

readset = saveset;

count = select(FD_SETSIZE, &readset, NULL, NULL, NULL);

if(count > 0)

{

int ret;

int mode;

int size;

if(FD_ISSET(sock, &readset))

{

ret = read(sock, &head, sizeof(head));

if((ret != sizeof(head)) || (LE32(head.magic) != JOY_MAGIC))

{

fprintf(stderr, "Error in socket %d, magic %08X\n", ret, head.magic);

flush_socket(sock);

break;

}

mode = LE32(head.mode);

size = LE32(head.size);

g_buffers[frame].head.mode = mode;

g_buffers[frame].head.size = size;

if(mode < 0)

{

if(g_context.args.video)

{

post_event(EVENT_ENABLE_SCREEN);

}

else

{

g_context.scron = 0;

}

}

else if(mode > 3)

{

/* Flush socket */

flush_socket(sock);

}

else

{

/* Try and read in screen */

/* If we do not get a full frame read and we timeout in quater second or so then

* reset sync as it probably means the rest isn't coming */

int loc = 0;

//fprintf(stderr, "Size %d\n", size);

while(1)

{

readset = saveset;

/* Should have a time out */

count = select(FD_SETSIZE, &readset, NULL, NULL, NULL);

if(count > 0)

{

ret = read(sock, &(g_buffers[frame].buf[loc]), size-loc);

if(ret < 0)

{

if(errno != EINTR)

{

perror("read:");

err = 1;

break;

}

}

else if(ret == 0)

{

fprintf(stderr, "EOF\n");

break;

}

//fprintf(stderr, "Read %d\n", loc);

loc += ret;

if(loc == size)

{

break;

}

}

else if(count < 0)

{

if(errno != EINTR)

{

perror("select:");

err = 1;

break;

}

}

}

if(!err)

{

if(frame)

{

post_event(EVENT_RENDER_FRAME_2);

}

else

{

post_event(EVENT_RENDER_FRAME_1);

}

frame ^= 1;

}

}

}

}

else if(count < 0)

{

if(errno != EINTR)

{

perror("select:");

err = 1;

}

}

}

return 0;

}

SDL_Surface *create_surface(void *buf, int mode)

{

unsigned int rmask, bmask, gmask, amask;

int currw, currh;

int bpp;

currw = PSP_SCREEN_W;

currh = PSP_SCREEN_H;

if(g_context.args.halfsize)

{

currw >>= 1;

currh >>= 1;

}

if(VERBOSE)

{

printf("Mode %d\n", mode);

}

switch(mode)

{

case 3:

rmask = LE32(0x000000FF);

gmask = LE32(0x0000FF00);

bmask = LE32(0x00FF0000);

amask = 0;

bpp = 32;

break;

case 2:

rmask = LE16(0x000F);

gmask = LE16(0x00F0);

bmask = LE16(0x0F00);

amask = 0;

bpp = 16;

break;

case 1:

rmask = LE16(0x1F);

gmask = LE16(0x1F << 5);

bmask = LE16(0x1F << 10);

amask = 0;

bpp = 16;

break;

case 0:

rmask = LE16(0x1F);

gmask = LE16(0x3F << 5);

bmask = LE16(0x1F << 11);

amask = 0;

bpp = 16;

break;

default: return NULL;

};

return SDL_CreateRGBSurfaceFrom(buf, currw, currh, bpp, currw*(bpp/8),

rmask, gmask, bmask, amask);

}

void save_screenshot(SDL_Surface *surface)

{

int i;

char path[PATH_MAX];

struct stat s;

/* If we cant find one in the next 1000 then dont bother */

for(i = 0; i < 1000; i++)

{

snprintf(path, PATH_MAX, "scrshot%03d.bmp", i);

if(stat(path, &s) < 0)

{

break;

}

}

if(i == 1000)

{

return;

}

if(SDL_SaveBMP(surface, path) == 0)

{

printf("Saved screenshot to %s\n", path);

}

else

{

printf("Error saving screenshot\n");

}

}

void mainloop(void)

{

SDL_Joystick *stick = NULL;

SDL_Surface *screen = NULL;

SDL_Surface *buf1 = NULL;

SDL_Surface *buf2 = NULL;

SDL_Thread *thread = NULL;

int currw, currh;

int sdl_init = 0;

int sock = -1;

unsigned int button_state = 0;

int currmode[2] = { 3, 3 };

int flags = SDL_HWSURFACE;

int pspflags = 0;

int showfps = 0;

Hit my limit deleted non essentials to controller mapping and left some display transmission info in.

But no one will probably even read this... I LOVE XDADEVELOPERS!