Jump to content

[SA] Detecting a player in garage. Cleo and function calls. [Solved]


vladvo

Recommended Posts

What I need: I need to check if player is inside a save garage. Just like is he in or is he out. Nothing more.
What I did: 

Found a function IsPointWithinHideOutGarage

0x448900  ; char __cdecl CGarages::IsPointWithinHideOutGarage(CVector *)

Ok. Next goes the CLEO testing  code:

{$CLEO .cs}
script_name 'testing'
0000:
:START
wait  0
if
Player.Defined($PLAYER_CHAR)
jf @START
:main
wait 0
00A0: store_actor $PLAYER_ACTOR position_to 1@ 2@ 3@
0AA7: call_function_return 0x448900 num_params 1 pop 1 1@v 6@  //passing 1@v as CVector
0AD1: show_formatted_text_highpriority "%d" time 20 6@
jump @main

Well, it works. The value changes and can be used to make the check required.
Results for 6@ below:

2621440   in Doherty
2621697   inside Doherty garage

2621696  in Los Santos
2621697   inside Los Santos garage

2636032   at the Airfield
2621697   inside Airfield garage

The solution is obvious - check if the function returns 2621697 and consider that CJ is inside the garage.
Still, I know that I am doing something wrong. Well, not right at least. ) I think my mistake is tied to the 'char' in char __cdecl

or the number I get in 6@ is some kind of flag or a pointer to something. Can anyone explain the result in 6@ and tell me how to handle it the right way ? 

 

Edited by vladvo
Wrong result for Doherty garage, typo
Link to comment
Share on other sites

Try formatting the value displayed as hex. I think the data will make more sense.

0AD1: show_formatted_text_highpriority "0x%08X" time 20 6@

 

Try removing the ambiguity of the long string. It will pass 4 vars instead of the expected 3. I'm not sure where that extra var will fall on the stack. It's possible for a float to contain a 00 byte which might terminate the string. The params need to be passed to the function in reverse order.

0AA7: call_function_return 0x448900 num_params 3 pop 3 3@ 2@ 1@ 6@ 

 

Posted results converted to hex (SB default hotkey is Ctrl-H).

0x00280000   in Doherty
0x002800E3   inside Doherty garage

0x00280100  in Los Santos
0x00280101   inside Los Santos garage

0x00283900   at the Airfield
0x00280101   inside Airfield garage

 

The data is starting to look useful, but I'd want to be sure of the correct params and order before drawing any conclusions.

 

Edited by OrionSR
Link to comment
Share on other sites

First of all - I made a typo in results (Doherty garage). The numbers are the same for all garages (2621697 = 0x00280101).

 

Crash with this line:

0AA7: call_function_return 0x448900 num_params 3 pop 3 3@ 2@ 1@ 6@


 

Quote

Try removing the ambiguity of the long string.

Made a small test with this code
 

00A0: store_actor $PLAYER_ACTOR position_to 1@ 2@ 3@
0AC7: 10@ = var 1@v pointer
10@ += 0x3 //0x4
0A8C: write_memory 10@ size 1 value 99 virtual_protect 0
0AA7: call_function_return 0x448900 num_params 1 pop 1 1@v 6@

As far as I know 1@v will use 1@ 2@ 3@ 4@ 
Not sure what is the offset for first one 0x0 or 0x1 ? Anyways - if I set 10@ to +0x3 and write 99 the code stops working. It just displays 2621440 and it doesn't change when entering garage. If I write 99 to +0x4 - everything works perfectly. I tried writing 99 and 0x0 to 4@ - no impact at all. So, I guess, there in no 00 string termination in this case. 
Not sure, but maybe that would help. Pseusocode from IDA. This "char result; // al " kinda puzzles me. Does it mean that result is returned as char ?

char __cdecl CGarages::IsPointWithinHideOutGarage(CVector *a1)
{
  CGarage *v1; // edx
  char result; // al

  v1 = CGarages::aGarages;
  while ( 2 )
  {
    switch ( v1->m_nType )
    {
      case 0x10:
      case 0x11:
      case 0x12:
      case 0x18:
      case 0x19:
      case 0x1A:
      case 0x1B:
      case 0x1C:
      case 0x1D:
      case 0x1E:
      case 0x1F:
      case 0x20:
      case 0x27:
      case 0x28:
      case 0x29:
      case 0x2A:
      case 0x2D:
        if ( !CGarage::IsPointInsideGarage(v1, a1->x, a1->y, a1->z) )
          goto LABEL_4;
        result = 1;
        break;
      default:
LABEL_4:
        if ( ++v1 < &dword_96EA78 )
          continue;
        result = 0;
        break;
    }
    return result;
  }
}

 

Edited by vladvo
Link to comment
Share on other sites

Yeah, I was looking at the pseudo code too. It looks like you need to pass a garage parameter but I'm not sure how. I reworked your script a few times but couldn't get consistent results. Sorry, these functions still confuse me to no end.

 

{$CLEO .cs}
script_name 'testing'
0000:
wait  6000
0ace: "Ready"
:START
wait 2000
if
Player.Defined($PLAYER_CHAR)
jf @START

00A0: store_actor $PLAYER_ACTOR position_to 1@ 2@ 3@
//00A0: store_actor $PLAYER_ACTOR position_to 3@ 2@ 1@   // swap param order for CVector
//0AA7: call_function_return 0x448900 num_params 4 pop 4 'LCKSfse' 3@ 2@ 1@ 30@  // crash
//0AA7: call_function_return 0x448900 num_params 4 pop 4 0x96D200 3@ 2@ 1@ 30@  // crash
//0AA7: call_function_return 0x448900 num_params 4 pop 4 3@ 2@ 1@ 0x96D200 30@ 
//0AA7: call_function_return 0x448900 num_params 4 pop 4 3@ 2@ 1@ 'LCKSfse' 30@ 
0AA7: call_function_return 0x448900 num_params 2 pop 2 1@v 'LCKSfse' 30@ 
//0AA7: call_function_return 0x448900 num_params 2 pop 2 1@v 0x96D200 30@ 
//0AA7: call_function_return 0x448900 num_params 2 pop 2 1@v 21 30@   // crash
//0AA7: call_function_return 0x448900 num_params 4 pop 4 3@ 2@ 1@ 21 30@  // crash
0AD1: show_formatted_text_highpriority "0x%08X" time 2000 30@
jump @START

Results were inconsistent - the value changed constantly but without regard to entering or exiting the garage.

Link to comment
Share on other sites

15 minutes ago, OrionSR said:

It looks like you need to pass a garage parameter but I'm not sure how.

You coding knowledge is far more superior than mine but with all due respect I'll argue about this statement.
There are two more functions:
char __thiscall CGarage::IsPointInsideGarage(int this, float, float, float)
int __stdcall CGarage::IsPointInsideGarage(RwV3d vec, float)
The first one, as I understand, clearly requires garage id (int this). Like in "does this point belong to exactly this garage".
The second one is a bit more complex and above my understanding.
The IsPointWithinHideOutGarage (the one we're using) is checking "does this point belong to any hideout garage"

Another point of interest is 0x0447120 ; _WORD *__cdecl CGarages::Init()
It reads from .data:0096C00B ??  _ZN8CGarages14PlayerInGarageE 
Unfortunately, I can't find any more info about this function.
 

Link to comment
Share on other sites

40 minutes ago, vladvo said:

Does it mean that result is returned as char ?

Hm... Maybe. Although it could be a single char; a byte. I wouldn't expect char to be an alphanumeric sort of value. The point being, it could be that the returned value includes garbage, and only the last byte has meaning. If this is the case, you could clear the rest of the returned value or use bit checks.

0B17: 6@ {&=} 0x000000FF
08B4:   test 6@ bit 0

Still, I'm expecting this function to require a passed garage ID of some sort. The pseudocode suggests it'll use a garage string or pointer to find an index used in the case switch.

Link to comment
Share on other sites

Whoops, now I'm double posting. Sorry.

 

2 minutes ago, vladvo said:

Another point of interest is 0x0447120 ; _WORD *__cdecl CGarages::Init()

This function is liable to reinitialize the garage structure for a new game start.

Link to comment
Share on other sites

10 minutes ago, OrionSR said:

Still, I'm expecting this function to require a passed garage ID of some sort. The pseudocode suggests it'll use a garage string or pointer to find an index used in the case switch.

But this line, when you hover over "IsPointInside" text in IDA pseudocode
 

if ( !CGarage::IsPointInsideGarage(&v1->m_vPosn.x, a1->x, a1->y, a1->z) )

shows  "bool __thiscall CGarage::IsPointInsideGarage(float *this, float a2, float a3, float a4)".
I think I'll insist that 'our' function is just wrapped over this one. It passes the 'case' param, coords and gives us an answer, received from IsPointInsideGarage(float *this, float a2, float a3, float a4)".

Edited by vladvo
Link to comment
Share on other sites

Okay, forget most of what I suggested.

 

If you put this line before printing results in your original script it should provide consistent results. 

{$USE bitwise}
0B17: 6@ {&=} 0xFF

Note: The & symbol is the standard opcode can't currently be parsed by SB, so I commented it out. This should be fixed in next SB release. Currently, SB is expecting an aDMA type variable number after &.

Edited by OrionSR
Link to comment
Share on other sites

7 minutes ago, OrionSR said:

it should provide consistent results. 

Thank you ! That would be fine. Although, I am still puzzled, why in "result = 0;" 0 is marked as char. What's the point of this ?

Link to comment
Share on other sites

20 minutes ago, vladvo said:

why in "result = 0;" 0 is marked as char. What's the point of this ?

char defines one byte, 8 bits. It's a data size like word or dword. It is not specific to ASCIII/ansi characters. It looks like only one char is written, and garbage may exist in the rest of the returned dword. The suggested bitwise AND operation should clear the garbage.

Link to comment
Share on other sites

1 minute ago, OrionSR said:

char defines one byte, 8 bits

Oh, I see. Thanks again. Already put everything in the mod - works perfectly.

Link to comment
Share on other sites

Quoting @Wesser in a locked topic:

Quote

For the record, (...), testing the LSB of the value returned by a function returning a bool is mandatory most of the time, because call-function commands give an int but usually only the al register is set leaving the high 24 bits of eax untouched which are likely to contain garbage.

So in this scenario, all you'd need to check is the least significant bit (LSB) for true or false, so a standard SCM bit test might be more appropriate in you mod.

if
  08B4:   test $390 bit 0
then

TRUE for all odd values.

Edited by OrionSR
Link to comment
Share on other sites

Thanks. That explains a lot. I think it should be 08B7, according to SBL.

08B4 IS_GLOBAL_VAR_BIT_SET_CONST

08B7 IS_LOCAL_VAR_BIT_SET_CONST

Link to comment
Share on other sites

57 minutes ago, vladvo said:

it should be 08B7

Whoops. Yeah, forgot about that. Uh-oh...

 

Okay, I'm safe. It looks like I grabbed that example out of opcode search without checking the var type. Thought maybe I grabbed it out of one of my scripts.

Link to comment
Share on other sites

You should also have a look at the structures tab of the IDA:

0A8D: 0@ = read_memory 0x465F05 size 4 virtual_protect 0    //  pointer to CCamera
0@ += 0x81C
0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0          //  m_pToGarageWeAreIn  CGarage
if 0@ <> 0
then
    //  we are inside a garage
    0@ += 0x4C
    0A8D: 0@ = read_memory 0@ size 1 virtual_protect 0          //  m_nType
    if 0@ == 5  //  pNs garage
    then

    end
end 

Other results:

Spoiler
1    Opens only for target vehicle (set with opcode 021B)
2    Bomb shop with timed detonator
3    Bomb shop with engine ignition detonation
4    Bomb shop with remote-control detonator
5    Spray garage
11    Strange garage. If anything touch it while is open, the garage will close. while closed no action is taken.
14    The garage will open for target car. Once stopped inside, the garage door will close and the player will be frozen. After a moment, the door will reopen. Driving away from the garage will close the garage and the garage will no longer accept the car.
15    Script garage. Script can open the garage, but can't close it.
16    Save garage (Ganton)
17    Save garage (Santa Maria Beach)
18    Save garage (Rockshore West)
19    Script garage. The garage is fully controlled by the script, the script should open and the script should close!
20    The garage will open for your car but it won't close by itself as long as the car is in the garage.
21    The garage will open for your car. Once inside you will be frozen until the door closes. The door stays closed. It won't accept your car again if you manage to get it out and attempt to drive back in.
23    The garage must be opened with a script command. Once inside you can leave your car in it and walk out. You will be frozen as the door closes. It will accept your car again if you manage to get it out and attempt to drive back in.
24    Save garage (Fort Carson)
25    Save garage (Verdant Meadows)
26    Save garage (Dillimore)
27    Save garage (Prickle Pine)
28    Save garage (Whitewood Estates)
29    Save garage (Palomino Creek)
30    Save garage (Redsands West)
31    Save garage (El Corona)
32    Save garage (Muholland)
33    Los Santos impound lot
34    San Fierro impound lot
35    Las Venturas impound lot
36    Loco Low Co (opens only for lowriders)
37    Wheel Arch Angels (opens only for street racers)
38    Transfender (opens for anything else)
39    Save garage (Calton Heights)
40    Save garage (Paradiso)
41    Save garage (Doherty)
42    Save garage (Hashbury)
43    Burglary
44    AT 400 hangar (garage door opens very slow)
45    Verdant Meadows hangar

 

Link to comment
Share on other sites

7 hours ago, Jack said:

You should also have a look at the structures tab of the IDA

Checked that. Found 0x26 m_bPlayerIsInGarage but it detects all garages. Odd thing is when you drive a bike fast m_bPlayerIsInGarage returns 256.
Anyways - IsPointWithinHideOutGarage is the best choice in my case. No need to sort out PnS and such.

 

Two more questions:
1. How do I find addresses like 0x465F05 ? The ones with pointers.
2. Why do you use 0x465F05 ? I found in SA Mem Addresses wiki that CCamera is at 0xB6F028. (And found it in IDA too.) What is the point to go the longer way instead of using already known address ?

Edited by vladvo
Link to comment
Share on other sites

44 minutes ago, vladvo said:

1. How do I find addresses like 0x465F05 ? The ones with pointers.

You start a binary search through the whole file and choose one of the results.

44 minutes ago, vladvo said:

2. Why do you use 0x465F05 ? I found in SA Mem Addresses wiki that CCamera is at 0xB6F028. (And found it in IDA too.) What is the point to go the longer way instead of using already known address ?

Pointer value might be changed either by fla or some other advanced code. It's less likely that a pointer itself will be changed. And when a ponter value changes for all of the results - the value (in this case 0xB6F028) becoms irrelevant.
So in my opinion pointers are better choice.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • 1 User Currently Viewing
    0 members, 0 Anonymous, 1 Guest

×
×
  • Create New...

Important Information

By using GTAForums.com, you agree to our Terms of Use and Privacy Policy.