// Transfer a full UMD rip over the wireless connection (takes < 5 minutes!)

// code by apv

// based on UMD/FLASH ripper by mph (mphtheone@hotmail.com -- code
// and comments were translated to english, apologies for that...)
// and the pspnet library (plus examples by others on how to use it)
// thanks & credits to all, including all anonymous code authors,
// plus FIXME, author of FileAssistant++


// Includes

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspumd.h>

#include <string.h>
#include <stdlib.h>
#include <pspiofilemgr_stat.h>
#include <pspwlan.h>
#include <pspnet_apctl.h>
#include <psputility_netparam.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>

#include "cfgdefaults.h"		// defaults for configuration file

// Various sceNetApctlGetState return values

#define APCTL_IDLE                      0
#define APCTL_INITIALIZING              1
#define APCTL_ASSOCIATING               2
#define APCTL_DHCPQUERY                 3
#define APCTL_READY                     4

// Defines

// media to use - note: other media than UMD have NOT been tested
// all code not relevant to UMD has been #ifdef'ed out by means of
// '#if 0' constructs
#define MEDIA_FLASH0			0x0
#define MEDIA_FLASH1			0x1
#define MEDIA_UMD			0x2
#define MEDIA_FLASH2			0x3
#define MEDIA_TEST			0xFF

#define SIZEBLOCK			(16 * 1024) // block size for copying

#define MEDIA				MEDIA_UMD
#define printf  			pspDebugScreenPrintf

// Globals 
					// tell assembler to make these global:
asm( ".global __lib_stub_top" );	// they are needed for patching the nlh
asm( ".global __lib_stub_bottom" );	// library jumps

// void *__lib_stub_top, *__lib_stub_bottom;
int mysocket = 0;			// my socket

// Defaults for params from .cfg file
char *serverip = DEFSERVERIP;		// the IP address of the server
char *servport = DEFSERVPORT;		// the port number the server listens to
char *skipupto = NULL;			// in case a previous run was stuck,
					// we may skip all files up to this one
static char *delims="\r\n";		// delimiters for strtok()
struct sockaddr_in addrTo;		// remote IP address and port

int BeginName;				// used in ScanFile_R

#ifdef RAPPORT_TXT			// #define this if you want the file
					// ms0:/RAPORT.TXT to be created
int fdg;				// used for RAPPORT.TXT
#endif


// apv: the PSP netlib I used seems to be not 100% compatible with the
// PSPSDK header files. If we include both, we end up having a number of
// type definition clashes. So, it is best to repeat a minimal set of
// definitions in "libnet/pspnet.h" here

int nlhLoadDrivers();
int nlhInit();
int nlhTerm();

// MODULE INITIALISATION

PSP_MODULE_INFO("UMDNETRIPPER", 0x1000, 2, 1);
PSP_MAIN_THREAD_ATTR(0x0);
PSP_MAIN_THREAD_STACK_SIZE_KB(0x200);


// Global functions

int CopyFileNet (const char *src, int sock)
{
 int fd;
 unsigned int filesize, x;
 char tab[SIZEBLOCK];
 char size[128];

 fd = sceIoOpen(src,PSP_O_RDONLY,0);
 if (fd < 0) return -1;


 filesize = sceIoLseek32(fd,0,PSP_SEEK_END);
 sceIoLseek32(fd,0,PSP_SEEK_SET);

 sprintf (size, "SIZE: %d\r\n\r\n", filesize);
 sceNetInetSend (mysocket, size, strlen (size), 0);

 while (filesize)
 {
  x = (filesize > SIZEBLOCK) ? SIZEBLOCK : filesize;

  sceIoRead(fd,tab,x);
  sceNetInetSend (mysocket, tab, x, 0);
  // sceKernelDelayThread (5 * 1000);

  filesize -= x;
 }

 sceIoClose(fd);

 return 0;
}

int ScanFile_R (char *dir)
{
 SceIoDirent files[128];
 char src[512];
 int x, fd;

 x = 0;
 fd = sceIoDopen(dir);
 if (fd < 0) return -1;

 while (sceIoDread(fd,&files[x]) > 0)
 {
  // store the source path into 'src'
  strcpy(src,dir);
  strcat(src,files[x].d_name);

  // print debugging info to the screen
  printf("%s to net://%s:%s\n",src,serverip,servport);

#ifdef RAPPORT_TXT
  // Ecrit dans RAPPORT.TXT
  sceIoWrite(fdg,"\r\n",strlen("\r\n"));
  sceIoWrite(fdg,src,strlen(src));
  sceIoWrite(fdg,"\r\n",strlen("\r\n"));
#endif

  SceMode sm = files[x].d_stat.st_mode;

  if (FIO_S_ISDIR (sm)) {
	if (files[x].d_name[strlen(files[x].d_name) - 1] == '.') break;

    printf("Type : Directory\n");

    sceNetInetSend (mysocket, "DIRECTORY: ", strlen ("DIRECTORY: "), 0);
    sceNetInetSend (mysocket, src, strlen (src), 0);
    sceNetInetSend (mysocket, "\r\n", 2, 0);

    strcat(src,"/");

    // recursive call to muself for each directory
    if (ScanFile_R(src))
	{
	 sceIoDclose(fd);
	 return -1;	
	}

	src[strlen(src) - 2] = 0;
  }
  else if (FIO_S_ISREG (sm)) {
    printf("Type : File\n");

#ifdef RAPPORT_TXT
    sceIoWrite(fdg,"Type : File\r\n",strlen("Type : File\r\n"));
#endif


    // if we were told to skip up to a certain file, ignore this file
    // unless it matches
    if (skipupto) {
     if (!strcmp (src, skipupto)) {
      skipupto = NULL;
     }
     else goto skipit;
    }

    sceNetInetSend (mysocket, "FILE: ", strlen ("FILE: "), 0);
    sceNetInetSend (mysocket, src, strlen (src), 0);
    sceNetInetSend (mysocket, "\r\n", 2, 0);

    // copy file to the network
    if (CopyFileNet (src, mysocket)) {
     sceIoDclose(fd);
     return -1;
    }
    sceNetInetSend (mysocket, "\r\n\r\n", 4, 0);

  }
  else if (FIO_S_ISLNK (sm)) {
    printf("Type : LINK\n");
  }

skipit: // we come here if we skipped the current file

  x++;
 }

 sceIoDclose(fd);

 return 0;
}

int exit_callback (void)
{
 nlhTerm();
 sceKernelExitGame();

 return 0;
}

int CallbackThread (SceSize args, void *argp)
{
 int cbid;


 cbid = sceKernelCreateCallback("Exit Callback", (void *) exit_callback, NULL);
 sceKernelRegisterExitCallback(cbid);

 sceKernelSleepThreadCB();

 return 0;
}

int SetupCallbacks (void)
{
 int thid = 0;


 thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0);
 if(thid >= 0) sceKernelStartThread(thid, 0, 0);

 return thid;
}

void readcfg (void)
{
 char data [SIZEBLOCK];
 static char srv [20], prt[20], skp [512];
 char chk [20];
 char *p, *p1, *p2, *p3, *p4;
 unsigned int a1, a2, a3, a4;
 size_t filesize, x;
 uint32_t n_addr;
 uint16_t n_port;
 int fd;

 fd = sceIoOpen (DEFCFGFILE, PSP_O_RDONLY, 0);
 if (fd < 0) {
  printf ("\"%s\": cannot open. Defaulting to \n", DEFCFGFILE);
 }
 else {
  filesize = sceIoLseek32 (fd, 0, PSP_SEEK_END);
  sceIoLseek32 (fd, 0, PSP_SEEK_SET);
  x = (filesize > SIZEBLOCK) ? SIZEBLOCK : filesize;
  sceIoRead (fd, data, x);
  sceIoClose (fd);

  printf ("Read %d bytes from \"%s\"\n", x, DEFCFGFILE);
  p = strtok (data, delims);
  while (p) {
    if (*p != '#') {
      if (!strncmp (p, "server=", 7)) {
        p += 7;
	if (strlen (p) > sizeof(srv)) {
	  printf ("Warning: IP address %s too long, ignored\n", p);
	}
	else {
	  strcpy (srv, p);
	  serverip = &srv[0];
	}
      }
      else if (!strncmp (p, "port=", 5)) {
        p += 5;
	if (strlen (p) > sizeof (prt)) {
	  printf ("Warning: port number %s too long, ignored\n", p);
	}
	else {
	  strcpy (prt, p);
	  servport = &prt[0];
	}
      }
      else if (!strncmp (p, "skipupto=", 9)) {
        p += 9;
	if (strlen (p) > sizeof (skp)) {
	  printf ("Warning: path %s too long, ignored\n", p);
	}
	else {
	  strcpy (skp, p);
	  skipupto = &skp [0];
	}
      }
      else {
      }
    }
    p = strtok (NULL, delims);
  }
 }

 // do some sanity checks and conversions
 strcpy (chk, serverip);
 p1 = strtok (chk,  ".");
 p2 = strtok (NULL, ".");
 p3 = strtok (NULL, ".");
 p4 = strtok (NULL, ".");
 if (!p1 || !p2 || !p3 || !p4) {
   printf ("warning: %s does not look like a valid address, defaulting to %s\n",
    serverip, DEFSERVERIP);
   serverip = DEFSERVERIP;
 }
 a1 = atoi (p1);
 a2 = atoi (p2);
 a3 = atoi (p3);
 a4 = atoi (p4);
 if (a1 > 255 || a2 > 255 || a3 > 255 || a4 > 255) {
   printf ("warning: invalid IP address %s, defaulting to %s\n",
    serverip, DEFSERVERIP);
   serverip = DEFSERVERIP;
 }

 n_addr = (a1 << 24) + (a2 << 16) + (a3 << 8) + a4;
 n_port = atoi (prt);

 addrTo.sin_family = AF_INET;
 addrTo.sin_addr.s_addr = (uint32_t) htonl (n_addr);
 addrTo.sin_port = htons (n_port);

 // print the configuration out
 printf ("Configuration:\n");
 printf ("Server:   %s\n", serverip);
 printf ("Port:     %s\n", servport);
 printf ("Skipping: %s\n", (skipupto)? skipupto: "not skipping any files");

}

int main (void)
{
 unsigned int x;
 int status;
 u32 i;
 netData data;
 int state;
 int scanfile_ret;

 pspDebugScreenInit();
 pspDebugScreenClear();
 SetupCallbacks();

 // read and process the config file
 readcfg ();

 // load the netlib drivers
 nlhLoadDrivers();

 // initialize the library
 if (nlhInit() != 0) {
  printf("nlhInit failed. Press HOME to exit\n");
  sceKernelExitDeleteThread(0);
  return 0;
 }

 // search among the first 100 connections to find one with a
 // static IP address

 for (i = 1; i < 100; i++) {
  if (sceUtilityCheckNetParam (i) != 0) {
   printf("sceUtilityCheckNetParam problem. Press HOME to exit\n");
   nlhTerm();
   sceKernelExitDeleteThread(0);
   return 0;
  }
  sceUtilityGetNetParam (i, PSP_NETPARAM_NAME, &data);
  printf("Connection #%d: %s ", i, data.asString);
  sceUtilityGetNetParam (i, PSP_NETPARAM_SSID, &data);
  printf("to SSID %s ", data.asString);
  if (sceUtilityGetNetParam (i, PSP_NETPARAM_IS_STATIC_IP, &data) != 0) {
   printf ("- skipping (need static IP address)\n");
   continue;
  }
  else {
   sceUtilityGetNetParam (i, PSP_NETPARAM_IP, &data);
   printf (" (IP address: %s)\n", data.asString);
   break;
  }
 }
 if (i== 101) {
  printf("No wireless connections found. Press HOME to exit\n");
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }

 // get the state of the wireless switch and the power-save wlan feature,
 // warn the user if they are not in the correct state

 if ((status = sceWlanGetSwitchState ()) == 0) {
  printf("Turn on the Wireless switch, then re-run. Press HOME to exit\n");
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }
 if ((status = sceWlanDevIsPowerOn ()) == 0) {
  printf("Turn on the Wireless switch, then re-run. Press HOME to exit\n");
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }

 // activate the wlan connection #i
 if ((status = sceNetApctlConnect (i)) != 0) {
  printf("Wireless connection #%d failed (0x%X). Press HOME to exit\n", i, status);
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }

 // wait for it 20 seconds to contact the wireless AP
 printf("Trying to connect to WLAN...");
 for (i = 0; i < 20; i++) {
  if ((status = sceNetApctlGetState (&state)) != 0) {
   printf("sceNetApctlGetState failed (0x%X). Press HOME to exit\n", status);
   nlhTerm();
   sceKernelExitDeleteThread(0);
   return 0;
  }
  if (state == APCTL_READY) {
   printf("\n");
   break;
  }
  printf ("%d ", 20 - i);
  sceKernelDelayThread (1000 * 1000);
 }
 if (i == 20) {
  printf("\nTimed out trying to connect. Press HOME to exit\n");
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }
 printf("WLAN connected successfully.\n");

 
 // get a TCP socket
 if ((mysocket = sceNetInetSocket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  printf("\nsceNetInetSocket failed (0X%X). Press HOME to exit\n", mysocket);
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }

 // and use it to connect to the specified server address and port
 printf ("Connecting to server %s... (press HOME to exit)\n", serverip);
 if ((status = sceNetInetConnect
      (mysocket, (struct sockaddr *) &addrTo, sizeof(addrTo))) < 0) {
  printf("\nCould not connect to server (0X%X). Press HOME to exit\n", status);
  nlhTerm();
  sceKernelExitDeleteThread(0);
  return 0;
 }
 printf ("Connected to server!");

#ifdef RAPPORT_TXT
 // open RAPPORT.TXT
 fdg = sceIoOpen("ms0:/RAPPORT.TXT",PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC,0777);
#endif

 // which media to rip? (note: apv: only UMD supported in this version)
 switch (MEDIA)
 {
  case MEDIA_UMD :
   printf("Waiting for UMD...\n");		// print message
   x = sceUmdCheckMedium(0);			// mount UMD under disc0:
   if (!(x)) {					// wait for the disc
     sceUmdWaitDriveStat(UMD_WAITFORDISC);
   }
   sceUmdActivate(1,"disc0:");			// activate it
   sceUmdWaitDriveStat(UMD_WAITFORINIT);	// wait for it to initialize

   printf("Ripping UMD...\n");


   BeginName = 5;				// skip 5 characters ("disc0")
   scanfile_ret = ScanFile_R("disc0:/");	// go ahead and rip it
  break;

 }

 // we are done! close the socket
 sceIoClose(mysocket);

#ifdef RAPPORT_TXT
 // close RAPPORT.TXT
 sceIoClose(fdg);
#endif

 // End
 printf("Rip terminated %s, please press HOME to quit\n",
  (scanfile_ret)? "unsuccessfully" : "successfully");

 sceKernelExitDeleteThread(0);

 return 0;
}
