Advertising (This ad goes away for registered users. You can Login or Register)

GTA LCS EU analog stick patch

Forum rules
Forum rule Nº 15 is strictly enforced in this subforum.
TheFloW
Guru
Posts: 57
Joined: Sun Jun 28, 2015 11:13 am

GTA LCS EU analog stick patch

Post by TheFloW » Tue Sep 27, 2016 2:56 pm

Introduction
More than 10 years have passed since the PSP was released. Since the day when developers could run unsigned codes, thousands of hombrew applications, games, plugins or other sorts of tools have been released to make the PSP great, literally everything of this device has been exploited to its fullest.
However there was something missing: right analog stick for camera movement.

PSP games were developed with special controls mapping in order to have camera movement. Some games use X, O, /\, [], some the digital pad, and then there are special ones which use L/R + left analog stick.
For the first and second case, mapping the right analog stick to the camera is easy enough, whereas for the L/R + left analog stick case, it isn't a good idea to just simulate the combo using the right analog stick. The reason therefore is logicial, yet many people oversee this little fact that you will not be able to use camera and walk and the same time using this kind of mapping; you can only do either of them.

After I have successfully gained native control over the pspemu on PS Vita, I have written the patch for my favourite PSP game: GTA LCS
In this topic I will explain how I have analysed and found the camera functions for this game.
The goal is to motivate people (developers) to write patches for their own games. Don't forget that every game is written differently, so you won't be able to find same code structure.

I tried to write the explaination in a simplictic way so anyone with enough programming knowledge, but no RE experience can follow and reproduce the final patch (I'm sorry if the tutorial is still hard to follow, as it is really difficult for me to describe the whole 2h process that I have done in words). If you are either a total beginner without any programming knowledge, or a talented reverse engineer, this tutorial is not addressed to you.

Some skills you must have:
- Intermediate programming knowledge
- Basic understanding of MIPS assembly.
- Basic maths

Other requirements:
- GTA LCS GER (ULES-00182 v1)
- UMDGen
- pspdecrypter for decrypting eboot.bin
- Prxtool to output eboot.bin as mips assembly code
- Pspdecompiler to output eboot.bin as pseudo-c

So let us begin:
1) Dump eboot.bin of the game using UMDGen
2) Decrypt it if it isn’t yet using pspdecrypter
3) Output the assembly code with: prxtool –w eboot.bin > eboot.asm
4) Find the module name with: prxtool –m eboot.bin
5) Output pseudo code with: pspdecompiler –c eboot.bin > eboot.c
6) Available as download is my included plugin sample, where you need to overwrite GTA3 by the module name you have found. If you're currently doing it with GTA LCS, you don't need to do this step as the module name of GTA LCS is already GTA3.


In GTA, you have got the camera mapped to L+left analog stick. Imagine this combo as such a code:

Code: Select all

SceCtrlData pad;
sceCtrlPeekBufferPositive(&pad, 1);
if (pad.Buttons & PSP_CTRL_LTRIGGER) {
...camera stuff using pad.Lx/pad.Ly
}
Where PSP_CTRL_LTRIGGER is 0x00000100

We are exactly looking for such a code in our game, but first of all we need to find where exactly the ctrl API is used within the code:
Those are the ctrl functions which are commonly used:

Code: Select all

- sceCtrlPeekBufferPositive (nid: 0x3A622550)
- sceCtrlPeekBufferNegative (nid: 0xC152080A)
- sceCtrlReadBufferPositive (nid: 0x1F803938)
- sceCtrlReadBufferNegative (nid: 0x60B81F86)
Find which of them (maybe more than only one) is used in your game by searching the nid code in the assembly file. For GTA you will find this:

Code: Select all

; ======================================================
; Subroutine sceCtrl_1F803938 - Address 0x00307934 
; Imported from sceCtrl
sceCtrl_1F803938:		; Refs: 0x00066CAC 0x001BE7E4 0x001BE81C 0x002D17F0 
	0x00307934: 0x03E00008 '....' - jr         $ra
	0x00307938: 0x00000000 '....' - nop        
Note that ‘Refs:’ lists all the addresses that jump to this function.
To find out which call is used for the camera movement, we can now do this trick:

Code: Select all

if (strcmp(modname, "GTA3") == 0) {
_sw(0, text_addr + ref0);
_sw(0, text_addr + ref1);
…
_sw(0, text_addr + refn);
}
We nop (no operation/set to zero) all the references so to prevent the ctrl function from working. Compile the plugin using the references that you have just added and launch gta using the patch.
Our goal is to find out which jump responsible for in-game controls is by testing the game running with the patch plugin. Instead of just testing them one by one, we'd rather do a manual binary search, simplified: you test half of them: if the buttons don't work in the game anymore, you have patched the good half. Ignore the other half then. Do the same again and again until you end up having the exact jump which is responsible for the camera.

For GTA, this is the address:

Code: Select all

0x00066CAC: 0x0C0C1E4D 'M...' - jal        sceCtrl_1F803938
Now you want to know where the start of its subroutine is by looking at the prxtool around this specific address.
Backward-search the term 'sub_' and you'll find sub_XXXXXXXX where XXXXXXXX is the address of the method.

In this case:

Code: Select all

; ======================================================
; Subroutine sub_00066C88 - Address 0x00066C88 
sub_00066C88:		; Refs: 0x00033FF0 
Now it's time we look at the pseudo code generated by pspdecompiler. We search the address of the subroutine 0x00066C88.

In the pseudo code you will find the ctrl function again, for GTA it's here:

Code: Select all

    var3 = sp + 0x00000014;
    var6 = sceCtrl_1F803938 (var3, 0x00000001); // Note that var3 is actually the SceCtrlData structure (pad).
Note that pad.Buttons entry is at pad+4 (sp + 0x00000018) and pad.Lx is at pad+8 (sp + 0x0000001C), pad.Ly at pad+9 (0x0000001D).

How is pad handled? Is it saved globally, is it copied to the function's parameters, or is it directly used in the code?

Later in this subroutine we find:

Code: Select all

    if (var6 == 0x00000000)							<- no analog
    {
      ((char *) sp)[18] = 0x00000080;
      var13 = 0x00000000;
      ((char *) sp)[19] = 0x00000080;
    }
    else
    {
      var13 = ((int *) sp)[6];						<- buttons
      var14 = ((unsigned char *) sp)[28];
      var15 = ((unsigned char *) sp)[29];
      ((char *) sp)[18] = var14;					<- left analog x
      ((char *) sp)[19] = var15;					<- left analog y
    }
    ((char *) sp)[16] = 0x00000080;					<- dummy right analog?
    var16 = ((int *) var2)[21];
    ((char *) sp)[17] = 0x00000080;					<- dummy right analog?
    ((int *) var2)[22] = ((var13 ^ var16) & var13);	<- pressed buttons
    ((int *) var2)[21] = var13;						<- current buttons
Where ((int *) sp)[6] is equivalent to *(int *)(sp + 6 * 4) (6 * 4) is 24, which in hex is 0x00000018. If you remember, that's the same value we have found when we looked at the ctrl's parameter.

At the beginning of the subroutine we see that var2 = arg1, so this means the ctrl functions are now copied to the first parameter of this subroutine. Let us rename sub_66C88 to readbuttons in the whole file. so we can easiely find it again.

If you search for this function you'll end up with the following subroutine: sub_33FE8 which calles

Code: Select all

readbuttons ((arg1 + 0x00000008));
You do the same process again: rename sub_33FE8 to something understandable, look where it is called.

Tip, it's 0x00294E84:
We can now see that the ctrl input is saved in the global variable 0x00356E14:

Code: Select all

    var11 = *((int *) 0x00356E14);
    var12 = 0x43000000;
    __asm__ ("mtc1       $a1, $fpr26;"
      : 
      : "=r"(0x43000000));
    if (var11 == 0x00000000)
    {
      sub_2F664C ();
      var11 = *((int *) 0x00356E14);
    }
    else
    {
    }
    sub_33FE8 (var11);
Later in this function we also see that there is a & 0x00000100 which, if you remember, is used to recognize if the L trigger is pressed.

Code: Select all

    var61 = *((int *) 0x00356E14);
    var62 = 0x00000000;
    if (!(var61 != 0x00000000))
    {
      sub_2F664C ();
      var61 = *((int *) 0x00356E14);
    }
   // check L trigger input
    var65 = ((int *) var61)[23]; // note that var61 is from the global variable 0x00356E14 that I have just mentioned
    if ((((0x00000000 < (var65 & 0x00000100))) & 0x000000FF) != 0x00000000)
    {
      var62 = 0x000000FF;
    }
    ((short *) var1)[5] = var62;         // set L trigger action in var1, where var1 is the first parameter of this subroutine
We see that ((short *) var1)[5] is set to 0xFF if L trigger is pressed. To confirm this without understanding much of the code, we can simply hook the whole method to our custom one, and then force this value. This trick can be applied to any subroutine and it's highly suggested that you do it for others too.

Write this in the OnModuleStart method (remove the ctrl nop patches again of course):

Code: Select all

HIJACK_FUNCTION(text_addr + 0x00294E84, buttonsToActionPatched, buttonsToAction);
and this in the scope:

Code: Select all

int (* buttonsToAction)(void *a1);

int buttonsToActionPatched(void *a1) {
	int res = buttonsToAction(a1);

	// L trigger counter
	((short *)a1)[0] = 7;
	
	// Simulate L trigger
	((short *)a1)[5] = 0xFF;

	// this blocks left and right analog movement
	// ((short *)a1)[1] = 0;

	// blocks up and down
	// ((short *)a1)[2] = 0;

	return res;
}
HIJACK_FUNCTION is already defined in the sample plugin.

Using this new patch in GTA you will now not be able to walk anymore but you can only move the camera.

The rest is now all the same, you find where 0x00294E84 (buttonsToAction) is called; how it is called.

We will find:

Code: Select all

/**
 * Subroutine at address 0x00292404
 */
int sub_292404 (int arg1)
{
  var1 = arg1 << 0x00000006;
  return ((var1 + (var1 + var1)) + 0x00385C60); // if arg1 is 0, the return is 0x00385C60
}

/**
 * Subroutine at address 0x00292420
 */
void sub_292420 ()
{
  sp = sp + 0xFFFFFFE0;
  ((int *) sp)[4] = ra;
  var3 = sub_292404 (0x00000000);
  buttonsToAction(var3, 0x00000000); // var3 is actually 0x00385C60
  var8 = sub_292404 (0x00000001);
  sub_292294 ((var8 + 0x00000002));
  var13 = sub_292404 (0x00000001);
  sub_292294 ((var13 + 0x00000034));
  ra = ((int *) sp)[4];
  sp = sp + 0x00000020;
  return;
}
If you look at the code you can see that buttonsToAction's first parameter is actually 0x00385C60


Rename all 0x00385C60 to buttons_to_action_variable or something else that you can easiely find.

Now do all the steps again and find out where exactly buttons_to_action_variable is used (better, search for sub_292404 (this function returns buttons_to_action_variable as I have mentioned before)). Remember, ((short *)buttons_to_action_variable)[5] is the LTRIGGER


The rest is now really a pain in the ***, you search this function and try to find somewhere where either analog stick or l trigger is used. I ended up finding these methods:

Code: Select all

/**
 * Subroutine at address 0x00292C5C
 */
int analogleftright (int arg1)
{
  var1 = *((unsigned char *) 0x003522B4);
  if (var1 == 0x00000000)
  {
    var5 = ((short *) arg1)[1];
  }
  else
  {
    var2 = ((short *) arg1)[12];
    var3 = ((short *) arg1)[11];
    var4 = var2 - var3;
    var5 = (((var4 + ((var4 >> 0x00000001) >> 0x0000001F)) >> 0x00000001) << 0x00000010) >> 0x00000010;
  }
  return var5;
}

/**
 * Subroutine at address 0x00292CA0
 */
int analogupdown (int arg1)
{
  var1 = *((unsigned char *) 0x003522B4);
  if (var1 == 0x00000000)
  {
    var5 = ((short *) arg1)[2];                             // remember this value in the buttonsToAction patch I showed you?
  }
  else
  {
    var2 = ((short *) arg1)[10];
    var3 = ((short *) arg1)[9];
    var4 = var2 - var3;
    var5 = (((var4 + ((var4 >> 0x00000001) >> 0x0000001F)) >> 0x00000001) << 0x00000010) >> 0x00000010;
  }
  return var5;
}

Code: Select all

/**
 * Subroutine at address 0x00293C28
 */
int ltriggerpressed (int arg1)
{
  var1 = *((int *) 0x00354C70);
  if (var1 != 0x00000000)
  {
    var5 = 0x00000000;
  }
  else
  {
    var2 = ((unsigned short *) arg1)[67];
    if (var2 != 0x00000000)
    {
      var5 = 0x00000000;
    }
    else
    {
      var3 = ((short *) arg1)[7];
      var4 = 0x00000000;
      if (var3 != 0x00000000)
      {
        var6 = ((short *) arg1)[5];                      // REMEMBER THIS?? ;)
        if (!(var6 == 0x00000000))
        {
          var4 = 0x00000001;
        }
      }
      else
      {
      }
      var5 = var4 & 0x000000FF;
    }
  }
  return var5;
}
I continued searching these functions and in the end found them in these camera functions:

Code: Select all

/**
 * Subroutine at address 0x00294C88
 */
int cameraLeftRight (int arg1)
{
  sp = sp + 0xFFFFFFE0;
  var1 = ((short *) arg1)[0];
  ((int *) sp)[4] = s0;
  var2 = arg1;
  ((int *) sp)[5] = ra;
  if (((var1 < 0x00000007)) != 0x00000000)
  {
    var26 = 0x00000000;
  }
  else
  {
    var5 = ltriggerpressed (var2);
    if (!(var5 == 0x00000000))
    {
      var8 = sub_1D18B8 ();
      var9 = ((int *) var8)[213];
      if (!(var9 != 0x00000001))
      {
        var12 = sub_1D18B8 ();
        var13 = ((int *) var12)[211];
        if (var13 == 0x00000010)
        {

        label16:
          ((short *) var2)[0] = 0x00000000;
        }
        else
        {
          var16 = sub_1D18B8 ();
          var17 = ((int *) var16)[211];
          if (!(var17 != 0x00000011))
            goto label16;
        }
      }
    }
    var18 = ((unsigned short *) var2)[65];
    __asm__ ("mtc1       $zr, $fpr12;");
    if (!(var18 < 0))
    {
      if (!(((var18 < 0x00000004)) == 0x00000000))
      {
        var21 = sub_292404 (0x00000000);
        var24 = analogleftright (var21);
        __asm__ ("mtc1       $v0, $fpr12;"
          : 
          : "=r"(var24));
        __asm__ ("cvt.s.w    $fpr12, $fpr12;");
      }
    }
    __asm__ ("mtc1       $zr, $fpr13;"
             "c.le.s     $fpr12, $fpr13;");
    __asm__ ("bc1t       0x00294D60;");
    {
      __asm__ ("trunc.w.s  $fpr12, $fpr12;"
               "mfc1       $a0, $fpr12;"
        : "=r"(var28));
      var26 = (var28 << 0x00000010) >> 0x00000010;
    }
    else
    {
      __asm__ ("trunc.w.s  $fpr12, $fpr12;"
               "mfc1       $a0, $fpr12;"
        : "=r"(var25));
      var26 = (var25 << 0x00000010) >> 0x00000010;
    }
  }
  var27 = ((int *) sp)[4];
  ra = ((int *) sp)[5];
  sp = sp + 0x00000020;
  return var26;
}

/**
 * Subroutine at address 0x00294D88
 */
int cameraUpDown (int arg1)
{
  sp = sp + 0xFFFFFFE0;
  var1 = ((short *) arg1)[0];
  ((int *) sp)[4] = s0;
  var2 = arg1;
  ((int *) sp)[5] = ra;
  if (((var1 < 0x00000007)) != 0x00000000)
  {
    var15 = 0x00000000;
  }
  else
  {
    var5 = ltriggerpressed (var2);
    if (!(var5 == 0x00000000))
    {
      ((short *) var2)[0] = 0x00000000;
    }
    var6 = ((unsigned short *) var2)[65];
    __asm__ ("mtc1       $zr, $fpr12;");
    if (!(var6 < 0))
    {
      if (!(((var6 < 0x00000004)) == 0x00000000))
      {
        var9 = sub_292404 (0x00000000);
        var12 = analogupdown (var9);
        __asm__ ("mtc1       $v0, $fpr12;"
          : 
          : "=r"(var12));
        var13 = *((unsigned char *) 0x003522B3);
        __asm__ ("cvt.s.w    $fpr12, $fpr12;");
        if (!(var13 == 0x00000000))
        {
          __asm__ ("neg.s      $fpr12, $fpr12;");
        }
      }
    }
    __asm__ ("mtc1       $zr, $fpr13;"
             "c.le.s     $fpr12, $fpr13;");
    __asm__ ("bc1t       0x00294E2C;");
    {
      __asm__ ("trunc.w.s  $fpr12, $fpr12;"
               "mfc1       $a0, $fpr12;"
        : "=r"(var17));
      var15 = (var17 << 0x00000010) >> 0x00000010;
    }
    else
    {
      __asm__ ("trunc.w.s  $fpr12, $fpr12;"
               "mfc1       $a0, $fpr12;"
        : "=r"(var14));
      var15 = (var14 << 0x00000010) >> 0x00000010;
    }
  }
  var16 = ((int *) sp)[4];
  ra = ((int *) sp)[5];
  sp = sp + 0x00000020;
  return var15;
}

If you have also found these functions by yourself, congratulations. These functions returns the lx, resp. ly in a range between -128 and +128, and they return 0 if analog stick is not moved

You can now map the camera to the digital pad, or to the right analog on ds3 (refer to DS3Remapper plugin).

Remember that this code is only for GTA LCS. You can definitely find the patches for other LCS regions, as the code structure will be similar. I believe that VCS will have the same code structure so you can easiely find them. For other games, I can't predict. Maybe they are harder to find, perhaps even easier. Even if you can't track the camera code until the end, any progress is welcomed to be shared, so other people can try to continue. To make this possible please post every progress that you have made.

I really look forward to more results, as this will allow us the really BEST GAMING EXPERIENCE for PPSSPP, PS Vita and also for PSPgo.

Furthermore, if there are any mistake in my explaination, please tell me.

I wish you the best luck.

Download
GTA LCS GER (ULES-00182 v1) analog stick mapped to digital pad sample:
https://drive.google.com/file/d/0B8d0Wy ... sp=sharing

Tasks:

If you have read and understood the whole explaination, there are some tasks for you:

* Find offsets for other regions and/or for GTA VCS.
** GTA LCS uses DOWN + R trigger + left analog stick to aim with a gun. Find the function by testing all analogleftright/analogupdown functions.
** Find patches for other games
Advertising
Last edited by TheFloW on Tue Sep 27, 2016 7:37 pm, edited 2 times in total.

User avatar
haxxey
Big Beholder
Posts: 567
Joined: Sat Jul 21, 2012 10:52 am
Location: Lurking /talk

Re: GTA LCS EU analog stick patch

Post by haxxey » Tue Sep 27, 2016 4:22 pm

Awesome write-up. Probably going to try patching VCS.
Advertising
We are such stuff as dreams are made on, and our little life is rounded with a sleep.

User avatar
reprep
Posts: 1074
Joined: Tue Dec 17, 2013 4:38 pm

Re: GTA LCS EU analog stick patch

Post by reprep » Tue Sep 27, 2016 6:16 pm

Any testers? I enabled the plugin on 6.60 pro cfw psp go and it doesn't remap the camera to d-pad on GTA LCS euro. Maybe problem is on my side.

EDIT: It doesnt work on ULES 00151, can anyone try it on ULES 00182 (The version patch is prepared for). ULES 00182 is the V2 of German release.
Last edited by reprep on Tue Sep 27, 2016 7:07 pm, edited 1 time in total.

R0ME0
Posts: 29
Joined: Fri Mar 27, 2015 4:35 pm

Re: GTA LCS EU analog stick patch

Post by R0ME0 » Tue Sep 27, 2016 7:00 pm

I'll try tonight I have LCS & PSP Go.
Then i'll try to see if I can find values for MH3U

Does this mean Adrenaline doesnt use the PSP Classics interface? Does that also maybe mean it runs at 333mhz? :)

User avatar
reprep
Posts: 1074
Joined: Tue Dec 17, 2013 4:38 pm

Re: GTA LCS EU analog stick patch

Post by reprep » Tue Sep 27, 2016 7:09 pm

I got the plugin enabled on ULES 00182, the game froze as soon as i pressed L + analog stick. Does anyone have the same result?

User avatar
ali_ihsan21
Posts: 681
Joined: Wed Jan 26, 2011 11:04 am

Re: GTA LCS EU analog stick patch

Post by ali_ihsan21 » Tue Sep 27, 2016 7:17 pm

I admire PSP enthusiasts, I believe they will make new PS vr compatible for PSP games nothing is impossible for them :D
PSP-1000 6.20 16 GB Pro C-2
PSP-go 6.20 32 GB LME 2.3 White
PSP-go 6.60 24 GB Pro C-2 / LME 2.3 Black
PS Vita Slim 8 GB 3.60 Henkaku
PS TV 3.60 Henkaku 1 TB Samsung Story
Thank you RepRep, Total_Noob, Suloku, Omega2058, TheFlow

TheFloW
Guru
Posts: 57
Joined: Sun Jun 28, 2015 11:13 am

Re: GTA LCS EU analog stick patch

Post by TheFloW » Tue Sep 27, 2016 7:39 pm

I forgot to mention that I was using GTA LCS GER (ULES-00182 v1) for the codes. Therefore, the plugin will only work for this version and region.

User avatar
reprep
Posts: 1074
Joined: Tue Dec 17, 2013 4:38 pm

Re: GTA LCS EU analog stick patch

Post by reprep » Tue Sep 27, 2016 7:47 pm

TheFloW wrote:I forgot to mention that I was using GTA LCS GER (ULES-00182 v1) for the codes. Therefore, the plugin will only work for this version and region.
Thanks, this version works. I now have the camera remapped to d-pad.

TheFloW
Guru
Posts: 57
Joined: Sun Jun 28, 2015 11:13 am

Re: GTA LCS EU analog stick patch

Post by TheFloW » Tue Sep 27, 2016 7:48 pm

Also, this is the code that I have used for the PS Vita demonstration:

Code: Select all

#define THRESHOLD 0x20

int cameraLeftRightPatched(void *a0) {
	// get rx first...

	rx -= 0x80;

	if (rx < -THRESHOLD || rx > THRESHOLD)
		return (int)rx;

	return 0;
}

int cameraUpDownPatched(void *a0) {
	// get ry first...

	ry = 255 - ry;

	ry -= 0x80;

	if (ry < -THRESHOLD || ry > THRESHOLD)
		return (int)ry;

	return 0;
}

User avatar
ali_ihsan21
Posts: 681
Joined: Wed Jan 26, 2011 11:04 am

Re: GTA LCS EU analog stick patch

Post by ali_ihsan21 » Tue Sep 27, 2016 8:04 pm

Is it possible to use the right analog of DS3 on PSP go for all angles instead of just 4 with the DS3 plugin we use now ?

Just wondering hypothesis.
PSP-1000 6.20 16 GB Pro C-2
PSP-go 6.20 32 GB LME 2.3 White
PSP-go 6.60 24 GB Pro C-2 / LME 2.3 Black
PS Vita Slim 8 GB 3.60 Henkaku
PS TV 3.60 Henkaku 1 TB Samsung Story
Thank you RepRep, Total_Noob, Suloku, Omega2058, TheFlow

Post Reply

Return to “Programming and Security”