Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /////////////////////////////////////
- // SuperVars Example Source File
- /////////////////////////////////////
- // Language:
- // SCR for Sanny Builder
- //
- // Description:
- // This script demonstrates how to create a simple save/load
- // system using SuperVars and CLEO 4 opcodes (IntOperations.cleo
- // is also required)
- //
- // Level:
- // Ini-termediate
- //
- // Compiled Script Checksum (CRC-32):
- // 0001C445
- //
- {$CLEO}
- {$VERSION 3.0.1000}
- SCRIPT_NAME 'savesys'
- CONST
- SuperVar = string // SB exploit
- NUM_VARS = 8 // lets have 8 normal vars
- NUM_SAVE_VARS = 8 // 8 saveable vars should also do..
- RampageData = 0@
- SaveVar = 31@
- // For our RampageData array:
- Score = RampageData // zero-index must use the same var the memory is stored in
- NumPedsKilled = -1@
- NumCarsWrecked = -2@
- NumCopsKilled = -3@
- NumGangstersKilled = -4@
- SaveVarPointer = -5@ // we can now store the SaveVar value here for easy access via SVar
- // For our SaveVar array:
- HighScore = SaveVar
- NumTimesRampaged = -1@
- TotalPedsKilled = -2@
- TotalCarsWrecked = -3@
- TotalCopsKilled = -4@
- TotalGangstersKilled = -5@
- TimesFailedRampage = -6@
- END
- VAR
- RampageData: array 4 of SuperVar
- SaveVar: array 4 of SuperVar
- END
- // Verify SuperVars installation...
- IF NOT IS_BIT_SET_L 38@ 9
- THEN
- PRINTSTRING "SuperVars is not installed"
- TERMINATE_THIS_CUSTOM_SCRIPT
- ELSE
- PRINTSTRING "SuperVars is running"
- END
- // Activate SuperVars for this script
- SET_BIT_L 38@ 8
- // Get the memory space for our vars
- RampageData = @VARIABLE_MEMORY
- // We can also use allocated memory for our SaveVar
- 0A90: 1@ = NUM_SAVE_VARS * 4 // make sure we have enough space (4 bytes per var)
- IF NOT ALLOCATE_MEMORY 1@ SaveVar
- THEN
- PRINTSTRING "ALLOCATE_MEMORY_FAILED"
- TERMINATE_THIS_CUSTOM_SCRIPT
- END
- // Make sure all the SaveVar data is null
- FOR 1@ = 0 TO NUM_SAVE_VARS
- 0006: SaveVar[1@] = 0
- END
- // Store a SaveVar pointer to an index of RampageData, just in case we have one and not the other
- 0085: RampageData[SaveVarPointer] = SaveVar
- // The "number of times visited safehouse" stat increases each time the game is saved
- // In other words, if it's 0, we shouldn't attempt to load, as the game must be new
- GET_INT_STAT 136 2@
- IF 2@ > 0
- THEN
- READ_MEMORY 0xBA68A7 1 0 2@ // read the selected menu slot
- // We have to pass the SVar to any functions we call that require it, which makes 0@ a good
- // choice for having as a global SVar, if we want to const it.
- CALL @Load 2 RampageData 2@
- END
- CONST
- // Some more vars (we obviously cant use 0@ or 31@ as they are already used)
- IsRampageOn = 1@
- RampageStartTime = 2@
- TempVal1 = 3@
- TempVal2 = 4@
- TempVal3 = 5@
- TempVal4 = 6@
- TempVal5 = 7@
- ShowSaveMenuStatus = 8@
- RAMPAGE_TIME = 60 // number of seconds of rampage time
- END
- // NULL vars we used above
- IsRampageOn = 0
- RampageStartTime = 0
- WHILE TRUE
- WAIT 0
- GOSUB @CheckSaveGame
- IF IS_PLAYER_PLAYING $PLAYER_CHAR
- THEN
- IF IsRampageOn == FALSE
- THEN
- IF TEST_CHEAT "RAMPAGE"
- THEN
- // display high score
- PRINT_BIG_FORMATTED "High Score: %d" TIME 3000 STYLE 6 INPUT SaveVar[HighScore]
- WAIT 3000
- // lets go on a rampage!
- IsRampageOn = TRUE
- GET_GAME_TIMER RampageStartTime
- RESET_NUM_OF_MODELS_KILLED_BY_PLAYER $PLAYER_CHAR
- // reset rampage data
- 0006: RampageData[Score] = 0
- 0006: RampageData[NumPedsKilled] = 0
- 0006: RampageData[NumCarsWrecked] = 0
- 0006: RampageData[NumCopsKilled] = 0
- 0006: RampageData[NumGangstersKilled] = 0
- // increase our save var stat
- 000A: SaveVar[NumTimesRampaged] += 1
- END
- ELSE
- GOSUB @Rampage
- END
- ELSE
- // if the player isnt playing, they probably died or got busted...
- IF IsRampageOn == TRUE
- THEN
- 000A: SaveVar[TimesFailedRampage] += 1
- IsRampageOn = FALSE
- END
- END
- END
- TERMINATE_THIS_CUSTOM_SCRIPT
- :Rampage
- GET_GAME_TIMER TempVal1
- 0062: TempVal1 -= RampageStartTime // get the time since the rampage started
- TempVal1 /= 1000 // convert milleseconds to seconds
- IF TempVal1 < RAMPAGE_TIME // the rampage started less than 60 seconds ago?
- THEN
- TempVal2 = RAMPAGE_TIME
- 0062: TempVal2 -= TempVal1 // so, as TempVal1 increases, TempVal2 decreases
- // Print the time we have left...
- PRINT_BIG_FORMATTED "Rampage Time Left: %d" TIME 1 STYLE 5 INPUT TempVal2
- 0006: RampageData[Score] = 0
- 0006: RampageData[NumPedsKilled] = 0
- 0006: RampageData[NumCarsWrecked] = 0
- 0006: RampageData[NumCopsKilled] = 0
- 0006: RampageData[NumGangstersKilled] = 0
- TempVal3 = 0x969A50 // we can use plain ol' memory addresses as SuperVars, remember?
- // 0x969A50 is an address that stores numbers of each model the
- // player has destroyed. in each index are two 2 byte values.
- // the first is the number of times player 1 has destroyed a model,
- // the second is the number of times player 2 has destroyed the model.
- FOR TempVal1 = 0 TO 1600 STEP 2 // loop through destroyed model counter range (each index refers to a model ID)
- {
- We can quickly access indexes of an array in the game memory using the start address
- as a SuperVar base.
- Although SuperVars cannot read/write less than 4 bytes at a time, we can still use 2 as
- the size specifier to get the right index, and use CLEO 4's 'BITWISE_AND', which comes
- with IntOperations.cleo, to read only the first 2 bytes of the data there...
- Similarly, 0xFF would mask only 1 byte. And 0xFFFFFFFF would mask all 4 bytes.
- The bitmask is what covers which bits to read, almost like how we use IS_BIT_SET.
- }
- BITWISE_AND TempVal3(TempVal1,2s) AND 0xFFFF TempVal2
- IF TempVal2 > 0
- THEN
- 0A91: TempVal4 = TempVal1 / 2 // get actual model index
- // ok, the model index in TempVal1 has been destroyed by the player since 'RESET_NUM_OF_MODELS_KILLED_BY_PLAYER'...
- IF TempVal4 < 300 // if the model index is under 300, it's probably a ped (see peds.ide)
- THEN
- // remember the technique to get a memory array index?
- // 0xA9B0C8 is an array of pointers to model information structs.
- TempVal5 = 0xA9B0C8
- // read the struct pointer and get an offset from that pointer
- 0A8E: TempVal5 = TempVal5(TempVal4,4s) + 0x28
- // we just got an offset of CPedModelInfo* from a ModelInfo array (CModelInfo * ModelInfo[20000])...
- // now store the value at that offset. we could also use READ_MEMORY, but SuperVars is quicker
- // however, only memory addresses which dont have virtual protection can be accessed this way
- 0085: TempVal5 = TempVal5(TempVal5,4s)
- // what we just did was read the default ped type of the ped model (CPedModelInfo+0x28)
- // now we can see what kind of ped the player killed
- IF TempVal5 == 6
- THEN
- // killed a cop!
- 005A: RampageData[NumCopsKilled] += TempVal2
- 000A: RampageData[Score] += 5
- ELSE
- IF AND
- TempVal5 >= 7
- TempVal5 <= 16
- THEN
- // killed a gangster!
- 005A: RampageData[NumGangstersKilled] += TempVal2
- 000A: RampageData[Score] += 2
- ELSE
- // killed some other ped...
- 005A: RampageData[NumPedsKilled] += TempVal2
- 000A: RampageData[Score] += 1
- END
- END
- ELSE
- // 400-614 is the vehicle model range
- IF AND
- TempVal4 >= 400
- TempVal4 < 615
- THEN
- // destroyed a vehicle
- 005A: RampageData[NumCarsWrecked] += TempVal2
- 000A: RampageData[Score] += 8
- END
- END
- END
- END
- // For debugging...
- PRINTNL
- PRINTINT2 "CARS_WRECKED" RampageData[NumCarsWrecked]
- PRINTINT2 "GANG_KILLED" RampageData[NumGangstersKilled]
- PRINTINT2 "COPS_KILLED" RampageData[NumCopsKilled]
- PRINTINT2 "PEDS_KILLED" RampageData[NumPedsKilled]
- PRINTINT2 "SCORE" RampageData[Score]
- ELSE
- // 60 seconds passed, so lets tally up the totals and end the rampage
- // storing it in the save var saves the data when the game is saved
- 005A: SaveVar[TotalPedsKilled] += RampageData[NumPedsKilled]
- 005A: SaveVar[TotalCopsKilled] += RampageData[NumCopsKilled]
- 005A: SaveVar[TotalGangstersKilled] += RampageData[NumGangstersKilled]
- 005A: SaveVar[TotalCarsWrecked] += RampageData[NumCarsWrecked]
- // did we beat our previously saved high score?
- IF 001D: RampageData[Score] > SaveVar[HighScore]
- THEN
- // High score!
- PRINT_BIG_FORMATTED "High Score: %d" TIME 4000 STYLE 5 INPUT RampageData[Score]
- 0085: SaveVar[HighScore] = RampageData[Score]
- ELSE
- PRINT_BIG_FORMATTED "Score: %d" TIME 4000 STYLE 5 INPUT RampageData[Score]
- END
- WAIT 4000
- PRINT_BIG_FORMATTED "Peds Killed: %d" TIME 4000 STYLE 5 INPUT RampageData[NumPedsKilled]
- WAIT 4000
- PRINT_BIG_FORMATTED "Cops Killed: %d" TIME 4000 STYLE 5 INPUT RampageData[NumCopsKilled]
- WAIT 4000
- PRINT_BIG_FORMATTED "Gangsters Killed: %d" TIME 4000 STYLE 5 INPUT RampageData[NumGangstersKilled]
- WAIT 4000
- PRINT_BIG_FORMATTED "Cars Wrecked: %d" TIME 4000 STYLE 5 INPUT RampageData[NumCarsWrecked]
- IsRampageOn = FALSE
- END
- RETURN
- :CheckSaveGame
- // Are we waiting for a save to happen?
- IF ShowSaveMenuStatus == 1
- THEN
- // Has the save happened?
- READ_MEMORY 0xBA68A6 1 0 TempVal1 // read the last menu screen ID
- IF OR
- TempVal1 == 18 // Save Complete Screen
- TempVal1 == 19 // Save Complete Screen 2
- THEN
- READ_MEMORY 0xBA68A7 1 0 TempVal1 // read the selected menu slot
- // Now we can use the save slot picked in the menu to save our game
- CALL @Save 2 RampageData TempVal1
- END
- ShowSaveMenuStatus = 0
- END
- // 0xBA67A7 == bShowSaveScreen - part of the 'CMenuManager' class
- READ_MEMORY 0xBA67A7 1 0 TempVal1
- IF TempVal1 == 1
- THEN
- // If bShowSaveScreen is 1, we need to wait for a save to happen
- ShowSaveMenuStatus = 1
- END
- RETURN
- // The comment trick can be used to preview function arguments.
- // See this topic: http://gtag.gtagaming.com/forums/index.php?showtopic=400
- :Save{__(SVar,_nSlot)__}
- // 0@ - RampageData SuperVar
- // 1@ - Save Slot
- // Because SaveVar is assigned to 31@, we'd have to pass a lot of vars to the function in order to get
- // the same use out of SaveVar. Instead, we can pass RampageData, which is already set to var 0@ and
- // get the copy of SaveVar we stored there...
- 0085: SaveVar = RampageData[SaveVarPointer]
- // A SuperVar trick we could use here is assigning 2@ to @TempMemory and using GET_VAR_POINTER on 2@(2@,4s)
- // which is the exact equivalent to using the command below. But we dont need to do that in this case...
- GET_LABEL_POINTER @TempMemory 2@
- STRING_FORMAT 2@ "cleo\saves\supervars%d.bin" 1@ // writes the formatted string to the space at :TempMemory
- // Open/create the save file for writing binary - note: the directory "cleo\saves" must already exist
- IF OPEN_FILE 2@ "wb" 3@
- THEN
- // Write each SaveVar index to the save file
- 0A90: 4@ = 4 * NUM_SAVE_VARS
- WRITE_TO_FILE 3@ 4@ SaveVar[SaveVar] // WRITE_TO_FILE can continuously write from any given var
- CLOSE_FILE 3@
- PRINTINT2 "SUPERVARS_SCRIPT_SAVED" 1@
- END
- RET 0
- :Load{__(SVar,_nSlot)__}
- // 0@ - RampageData SuperVar
- // 1@ - Save Slot
- // We basically follow a similar procedure as for saving here...
- 0085: SaveVar = RampageData[SaveVarPointer]
- GET_LABEL_POINTER @TempMemory STORE_TO 2@
- STRING_FORMAT 2@ "cleo\saves\supervars%d.bin" 1@
- // Open the save file for reading binary, if it exists - if not, no data will be loaded
- IF OPEN_FILE 2@ "rb" 3@
- THEN
- // Make sure the size of the file is the same as the size of our var space
- 0A90: 4@ = 4 * NUM_SAVE_VARS
- GET_FILE_SIZE 3@ STORE_TO 5@
- IF 003B: 4@ == 5@
- THEN
- // Read each SaveVar index from the file
- 6@ = 0
- WHILE NOT IS_END_OF_FILE_REACHED 3@
- READ_FROM_FILE 3@ 4 STORE_TO SaveVar[6@]
- 6@ += 1
- END
- PRINTINT2 "SUPERVARS_SCRIPT_LOADED" 1@
- ELSE
- PRINTINT2 "SUPERVARS_SAVE_INCOMPATIBLE" 1@
- END
- CLOSE_FILE 3@
- END
- RET 0
- // This label is used as variable memory. The HEX..END structure
- // allows raw hex data to be inserted. We're inserting a lot of 00's.
- // Each 00 is worth 1 byte of data. A basic variable uses 4 bytes of
- // data, so for every 00 00 00 00 we get 1 variable to use...
- //
- // This also means that every SVar Index starts out with the value 0
- //
- // This is 40 bytes of blank space... We dont have to use it all, but
- // the amount of space should be equal to or higher to the space we
- // need to store our vars
- //
- :VARIABLE_MEMORY
- HEX
- 00 00 00 00 // SVar Index 0
- 00 00 00 00 // SVar Index 1
- 00 00 00 00 // SVar Index 2
- 00 00 00 00 // SVar Index 3
- 00 00 00 00 // SVar Index 4
- 00 00 00 00 // SVar Index 5
- 00 00 00 00 // SVar Index 6
- 00 00 00 00 // SVar Index 7
- 00 00 00 00 // SVar Index 8
- 00 00 00 00 // SVar Index 9
- END
- // This is some space where we can store anything we want.
- // We'll use it for storing a formatted string temporarily.
- // We have 128 bytes here, so we could have a 127 char string stored here...
- //
- :TempMemory
- HEX
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- END
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement