Punkline
Dr. Frankenstack
- Joined
- May 15, 2015
- Messages
- 423
This code adds space to the end of each player data table as it is allocated in the heap, and uses a DTag Stack container to manage the space in a way that promotes compatibility between any codes that make use of it.
It works by utilizing MCM standalone functions in order to create a set of specialized (and very small) functions that make use of the DTag Stacks module as a foundation.
https://i.imgur.com/4V5djVW.png
!IMPORTANT! - Make sure the DTag Stacks module is installed before this code to avoid dependency issues.
Edit 9/21/2017 - Fixed typo in DOL mod
Download version 1.10 here:
---
The default player padding initialization extends the region from 0x2400 bytes to 0x6000 bytes; which may be modified through <pdtGlobals> in the DOL mod.
New player data offsets, by default <pdtGlobals> params:
0x2450 - disjointed allocation stack header, 0x30 bytes in length
0x2480 - start of unused padding area, 0x380 bytes in length.
0x2800 - start of allocation stack container, 0x3800 bytes in length
0x6000 - end of player data and stack container
The unused padded area can be directly written to if desired. This is not recommended for the sake of sharing; as it will not be compatible with other codes that do so. It may still serve as useful for personal mods and experiments, however.
Rather than use these offsets directly, the code gives you a set of functions that can manage the stack without any knowledge of table’s displacement offset. For the most part, these are the only 3 things that you will need:
r3 = player data start address
r4 = 32-bit allocation tag (used in search identity and/or push definitions)
r5 = size of allocation in bytes (used in search identity and/or push definitions)
The DTag Stacks module was designed to use these 3 arguments in common with all included stack operation functions. These essentially just comprise of Seek, Push, and combinations/permutations of the two.
An example using the included <pdtSafeSeekPush> function:
Gonna use Zelda magic to explain this.
Farore’sWind Interface :
SeekPush functions can initialize and recall a waypoint from the same input arguments. These two functionalities are both accessed from a singlespell set of calling instructions.
Zelda Sheik @DRGN had a great idea for using ocarina melodies ascii strings as a way of providing a key that confirms a valid location for codes intending to read structured data. This inspired the container module used to create this interface; limiting the keys to 32-bit numbers for efficient scans.
This interface may be used to cover the step of setting up an allocation, and later the step of recalling it. No extra written code is required to cover both cases.
Din’sFire Allocation :
If the specified tag doesn’t exist in this player’s dictionary, then -- provided that there’s room left in the stack for a push of the specified <alloc size> + 8-byte tag -- the code will carve out a chunk of the padding left in the player data table, and designate a new symbol for it in the tag dictionary
The number of bytes specified for the allocation will be used to push the decrementing allocation stack frame inside the container if it is not set to 0. Since the container is push-only, the tag’s order in the dictionary can optionally be used to later recover its Push size during Seek operations.
Nayru’sLove Logic :
A byte number provided in r5 will cause the allocation size of a push symbol to become part of the scan identity.
This somewhat reduces the chance of running into compatibility conflicts that would normally be caused by multiple codes using the same 32-bit tag identifier. Scans will skip this distinction if r5 is provided as ‘0’ -- checking only for 32-bit tag identifiers.
With SafeSeekPush, these cases where more than one identical tag/size instances are found will cause the code to detect a null return instead of a valid address; resulting in the code disabling itself.
This measure protects the game from potential complications that might otherwise cause a crash. A number of other predictable error cases will recycle this null mechanic, allowing each to be checked (simultaneously) with a single logical comparison after return. The type of error can be derived from return value in r6, if necessary.
---
So let’s break down what those 6 call instructions are doing.
The bl <pdtSeekPush> line is a special branch syntax in MCM. The lines above it specify our r3, r4, and r5 arguments.
Our inputs for the call in this example are:
r3 = ( player data start address )
r4 = 0x80280000 ( a “name” )
r5 = 0x70 ( number of bytes we want for our allocation )
The returned values are:
r3 = Address of tagged allocation, (or Null for Error)
r4 = Allocation Tag
r5 = Allocation Size
r6 = Player Data Offset of Allocation start, ( or Error Code )
r7 = 1-based duplicate instance ID ( or Null for new Push )
r8 = 1-based duplicate instance count
r9 = 0-based dictionary ID ( dictionary element index )
If the specified r3, r4, and r5 inputs can’t provide a valid match, and the DUPE_LIMIT was not exceeded; then the function will push a new tag/allocation matching those specifications and return the new tag instead; if possible.
In all future calls using the same specifications -- so long as the player has remained allocated in the heap consistently between calls and no other codes have registered an identical tag -- the return values will consistently reference a symbol of the same push in the stack.
So if this example call were to run each frame, the first call would create the allocation and use that as the return value. Any following calls will cause the allocation pushed in the first call to be recalled -- save for any errors.
The cmpwi r3, 0 at the end is a check for null. It’s possible for the stack to be full when attempting a push, a seek to return 0 matches, too many matches, etc. In these null cases, we branch to a handle that aborts the execution of our code. This disables the code in cases where an address could not be returned for any reason.
In cases where the returned r3 address is null, the returned r6 value will also be <= 0 as a way of representing an error code.
As of DTag Stacks 0.27, these are:
0 - NO_MATCH_ERROR (Seek operations)
-1 - DUPE_LIMIT_ERROR (Seek operations)
-2 - VALIDATION_ERROR (Seek and Push operations)
-3 - NO_POINTER_ALERT (Seek and Push operations)
-4 - PUSH_LIMIT_ERROR (Push operations)
-5 - READ_ONLY_ERROR (Push operations)
These don’t need to be checked for, but may optionally be used to handle specific cases.
For instance, the “SeekPush” operations included in this module are just “Seek” operations that handle a NO_MATCH_ERROR with a “Push” operation.
In Seek operations, r6 is an additional argument ( 1-based ID ) that can make a scan a bit more specific. In plain Seek and SeekPush functions, this ID can be used to specify a place in an index of “duplicate” instances as an additional match condition.
In PDT containers, this means specifying which of two or more identically named/sized symbol instances to return. Only one tag is returned from the Seek operation, but the 1-based duplicate ID is returned as r7 -- with r8 as the total duplicate count.
By specifying 0 as r6, Seek operations will skip the process of looking for duplicates and use the first matching instance as the only return condition.
The “Quick” versions of Seek and SeekPush merely branch into the plain versions of the function with 0 as a default value for r6 -- invoking the above behavior without the need to specify it.
By specifying a negative number in r6, its absolute value will be treated like a positive ID -- however if any additional instances are found beyond this matching ID, it will throw a DUPE_LIMIT_ERROR instead. This is meant to allow codes to be safely handled in cases where there may be a conflict of tag identities between codes.
The “Safe” versions of Seek and SeekPush branch into the plain versions of the functions with -1 as a default value for r6.
---
Some further documentation, for those intending to develop with these functions:
It works by utilizing MCM standalone functions in order to create a set of specialized (and very small) functions that make use of the DTag Stacks module as a foundation.
https://i.imgur.com/4V5djVW.png
!IMPORTANT! - Make sure the DTag Stacks module is installed before this code to avoid dependency issues.
Edit 9/21/2017 - Fixed typo in DOL mod
Download version 1.10 here:
Code:
-==-
PDT Stacks 1.10
- Requires DTag Stacks Module
Pad in-game player data table allocations by a 0x3800 bytes, and use DTag Stack container to allocate tagged pushes in padding. Allows player codes to be written in a way that saves persistant data.
[Punkline]
1.02 ----- 0x800679bc --- 388023ec -> branch
bl <pdtGlobals>
7C8802A6 80840014
00000000
1.02 ----- 0x80068eec --- 3c808046 -> Branch
7C7E1B78
bl <pdtGlobals>
7C8802A6 38A00000
7FC6F378 80E40010
81040014 8064000C
7C63F214
bl <dtagInit>
3C808046 7FC3F378
00000000
<pdtGlobals> All
4E800021
b <dtagSeek>
b <dtagPush>
b <dtagSeekPush>
00002400 00002800
00006000
<pdtDispatch> All
7C0802A6 90010004
9421FFF8
bl <pdtGlobals>
7CE802A6 8167000C
7C635A14 7D4A3A14
7D4803A6 4E800021
38210008 80010004
7C0803A6 4E800020
<pdtSeek> All
39400000
b <pdtDispatch>
<pdtPush> All
39400004
b <pdtDispatch>
<pdtSeekPush> All
39400008
b <pdtDispatch>
<pdtSafeSeek> All
38C0FFFF
b <pdtSeek>
<pdtQuickSeek>All
38C00000
b <pdtSeek>
<pdtSafeSeekPush> All
38C0FFFF
b <pdtSeekPush>
<pdtQuickSeekPush> All
38C00000
b <pdtSeekPush>
Code:
-==-
PDT Stacks 1.10
- Requires DTag Stacks Module
Pad in-game player data table allocations by a 0x3800 bytes, and use DTag Stack container to allocate tagged pushes in padding. Allows player codes to be written in a way that saves persistant data.
[Punkline]
1.02 ----- 0x800679bc --- 388023ec -> branch
# 800679bc : li r4, 9196
# is executed before players are allocated,
# loading hardcoded immidate 0x23EC
# -- 0x23EC normally becomes size player data table
# -- we set it to whatever is specified in <pdtGlobal>
.set xEnd, 0x14
bl <pdtGlobals>
mflr r4 # pad the end of player data by (size)
lwz r4, xEnd(r4) # r4 = specified size in globals
.long 0
1.02 ----- 0x80068eec --- 3c808046 -> Branch
# 80068eec : lis r4, 0x8046
# this executes just after each player's HSD_MemAlloc
# -- we use the opportunity to initialize the stack and
# zero out the padded space
# r3 contains fresh player data table start address
# it will be saved to r30, so we store it there for now
.set xHead, 0x0C # these are offsets;
.set xStart, 0x10 # see <pdtGlobals> for params
.set xEnd, 0x14 # can be edited procedurally
mr r30, r3
bl <pdtGlobals> # call hits blrl and returns immediately
mflr r4 # pdt globals address becomes init tag
li r5, 0 # ordered, sized, read/write stack
mr r6, r30 # base address == player data start
lwz r7, xStart(r4) # start offset will become address
lwz r8, xEnd (r4) # end offset will become address
lwz r3, xHead (r4)
add r3, r3, r30 # calculate header address
bl <dtagInit> # initialize stack in player data
lis r4, 0x8046 # original instruction
mr r3, r30 # restore original context in r3
.long 0
<pdtGlobals> All
blrl
# 0x0
b <dtagSeek> # dispatch will use these when setting
b <dtagPush> # up player header for all pdt funcs
b <dtagSeekPush> # (this saves a few lines of code)
# 0xC
.long 0x2400 # 0x0C - header start offset
.long 0x2800 # 0x10 - container start offset
.long 0x6000 # 0x14 - container ending offset
# start is {inclusive} ; end is {}exclusive
# PDT - player data tags
# -- a player data version of the dtag stack (type 0)
<pdtDispatch> All
# handle quick operation that uses globals to specify
# location of header from a player data start address.
# dispatch removes need for multiple runtime (sp) frames
# r3 = player data start address
# r4 = tag for op
# r5 = size for op
# r6 = dupeArg, if applicable
# r10 = dispatch offset from blrl in pdtGlobals
.set rHead, 3
.set rInit, 7
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x8(sp) # runtime stack activation
bl <pdtGlobals> # get globals table
mflr rInit # pdt uses table address as init tag
lwz r11, 0xC(rInit) # load header offset from
add rHead, rHead, r11 # relocate offset using r3 address
add r10, r10, rInit # load branch-in func blrl call
mtlr r10
blrl # dispatch
_return:
addi sp, sp, 0x8 # runtime stack deactivation
lwz r0, 0x4(sp)
mtlr r0
blr
# cute little functions~
<pdtSeek> All
li r10, 0
b <pdtDispatch>
<pdtPush> All
li r10, 4
b <pdtDispatch>
<pdtSeekPush> All
li r10, 8
b <pdtDispatch>
<pdtSafeSeek> All
li r6, -1
b <pdtSeek>
<pdtQuickSeek>All
li r6, 0
b <pdtSeek>
<pdtSafeSeekPush> All
li r6, -1
b <pdtSeekPush>
<pdtQuickSeekPush> All
li r6, 0
b <pdtSeekPush>
---
The default player padding initialization extends the region from 0x2400 bytes to 0x6000 bytes; which may be modified through <pdtGlobals> in the DOL mod.
New player data offsets, by default <pdtGlobals> params:
0x2450 - disjointed allocation stack header, 0x30 bytes in length
0x2480 - start of unused padding area, 0x380 bytes in length.
0x2800 - start of allocation stack container, 0x3800 bytes in length
0x6000 - end of player data and stack container
The unused padded area can be directly written to if desired. This is not recommended for the sake of sharing; as it will not be compatible with other codes that do so. It may still serve as useful for personal mods and experiments, however.
Rather than use these offsets directly, the code gives you a set of functions that can manage the stack without any knowledge of table’s displacement offset. For the most part, these are the only 3 things that you will need:
r3 = player data start address
r4 = 32-bit allocation tag (used in search identity and/or push definitions)
r5 = size of allocation in bytes (used in search identity and/or push definitions)
The DTag Stacks module was designed to use these 3 arguments in common with all included stack operation functions. These essentially just comprise of Seek, Push, and combinations/permutations of the two.
An example using the included <pdtSafeSeekPush> function:
Code:
# r31 is assumed to hold external player entity address
lwz r3, 0x2C(r31) # load internal player data table
lis r4, 0x8028 # specify a tag
li r5, 0x70 # specify a size
bl <pdtSafeSeekPush> # call SeekPush
cmpwi r3, 0 # check for null
beq- _return # return if null
_code:
# code functionality goes here
_return:
blr
Farore’s
SeekPush functions can initialize and recall a waypoint from the same input arguments. These two functionalities are both accessed from a single
This interface may be used to cover the step of setting up an allocation, and later the step of recalling it. No extra written code is required to cover both cases.
Din’s
If the specified tag doesn’t exist in this player’s dictionary, then -- provided that there’s room left in the stack for a push of the specified <alloc size> + 8-byte tag -- the code will carve out a chunk of the padding left in the player data table, and designate a new symbol for it in the tag dictionary
The number of bytes specified for the allocation will be used to push the decrementing allocation stack frame inside the container if it is not set to 0. Since the container is push-only, the tag’s order in the dictionary can optionally be used to later recover its Push size during Seek operations.
Nayru’s
A byte number provided in r5 will cause the allocation size of a push symbol to become part of the scan identity.
This somewhat reduces the chance of running into compatibility conflicts that would normally be caused by multiple codes using the same 32-bit tag identifier. Scans will skip this distinction if r5 is provided as ‘0’ -- checking only for 32-bit tag identifiers.
Code:
if: then: FALSE , TRUE
r4 = ‘tag_’ ‘tag_’ , ‘tag_’
r5 = 0x70 0x48 , 0x70
if: then: TRUE , TRUE
r4 = ‘tag_’ ‘tag_’ , ‘tag_’
r5 = 0 0x48 , 0x70
This measure protects the game from potential complications that might otherwise cause a crash. A number of other predictable error cases will recycle this null mechanic, allowing each to be checked (simultaneously) with a single logical comparison after return. The type of error can be derived from return value in r6, if necessary.
---
So let’s break down what those 6 call instructions are doing.
The bl <pdtSeekPush> line is a special branch syntax in MCM. The lines above it specify our r3, r4, and r5 arguments.
Our inputs for the call in this example are:
r3 = ( player data start address )
r4 = 0x80280000 ( a “name” )
r5 = 0x70 ( number of bytes we want for our allocation )
The returned values are:
r3 = Address of tagged allocation, (or Null for Error)
r4 = Allocation Tag
r5 = Allocation Size
r6 = Player Data Offset of Allocation start, ( or Error Code )
r7 = 1-based duplicate instance ID ( or Null for new Push )
r8 = 1-based duplicate instance count
r9 = 0-based dictionary ID ( dictionary element index )
If the specified r3, r4, and r5 inputs can’t provide a valid match, and the DUPE_LIMIT was not exceeded; then the function will push a new tag/allocation matching those specifications and return the new tag instead; if possible.
In all future calls using the same specifications -- so long as the player has remained allocated in the heap consistently between calls and no other codes have registered an identical tag -- the return values will consistently reference a symbol of the same push in the stack.
So if this example call were to run each frame, the first call would create the allocation and use that as the return value. Any following calls will cause the allocation pushed in the first call to be recalled -- save for any errors.
The cmpwi r3, 0 at the end is a check for null. It’s possible for the stack to be full when attempting a push, a seek to return 0 matches, too many matches, etc. In these null cases, we branch to a handle that aborts the execution of our code. This disables the code in cases where an address could not be returned for any reason.
In cases where the returned r3 address is null, the returned r6 value will also be <= 0 as a way of representing an error code.
As of DTag Stacks 0.27, these are:
0 - NO_MATCH_ERROR (Seek operations)
-1 - DUPE_LIMIT_ERROR (Seek operations)
-2 - VALIDATION_ERROR (Seek and Push operations)
-3 - NO_POINTER_ALERT (Seek and Push operations)
-4 - PUSH_LIMIT_ERROR (Push operations)
-5 - READ_ONLY_ERROR (Push operations)
These don’t need to be checked for, but may optionally be used to handle specific cases.
For instance, the “SeekPush” operations included in this module are just “Seek” operations that handle a NO_MATCH_ERROR with a “Push” operation.
In Seek operations, r6 is an additional argument ( 1-based ID ) that can make a scan a bit more specific. In plain Seek and SeekPush functions, this ID can be used to specify a place in an index of “duplicate” instances as an additional match condition.
In PDT containers, this means specifying which of two or more identically named/sized symbol instances to return. Only one tag is returned from the Seek operation, but the 1-based duplicate ID is returned as r7 -- with r8 as the total duplicate count.
By specifying 0 as r6, Seek operations will skip the process of looking for duplicates and use the first matching instance as the only return condition.
The “Quick” versions of Seek and SeekPush merely branch into the plain versions of the function with 0 as a default value for r6 -- invoking the above behavior without the need to specify it.
By specifying a negative number in r6, its absolute value will be treated like a positive ID -- however if any additional instances are found beyond this matching ID, it will throw a DUPE_LIMIT_ERROR instead. This is meant to allow codes to be safely handled in cases where there may be a conflict of tag identities between codes.
The “Safe” versions of Seek and SeekPush branch into the plain versions of the functions with -1 as a default value for r6.
---
Some further documentation, for those intending to develop with these functions:
<pdtPush>
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
the container dictionary is pushed by 8 bytes to make room for a new tag
the 32-bit tag is written at offset 0x0 of the dictionary element
if r5 is set to a number greater than 0,
then the container pushes the stack frame to make room for that many bytes.
the address of the new push is written at offset 0x4 of the dictionary element
the frame address is returned in r3
else, if r5 == 0
then the unallocated dictionary element address is returned in r3
r6 provides an error code to indicate that pushed tag contains a null pointer
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r9 = dictionary ID
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
the container dictionary is pushed by 8 bytes to make room for a new tag
the 32-bit tag is written at offset 0x0 of the dictionary element
if r5 is set to a number greater than 0,
then the container pushes the stack frame to make room for that many bytes.
the address of the new push is written at offset 0x4 of the dictionary element
the frame address is returned in r3
else, if r5 == 0
then the unallocated dictionary element address is returned in r3
r6 provides an error code to indicate that pushed tag contains a null pointer
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r9 = dictionary ID
<pdtSeek>
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
r6 = duplicate instance count/limit
for each tag in this player’s dictionary:
if the tag matches r4,
-- if the size matches r5; OR if r5 == 0
---- if r6 == 0, then return match
---- else, if abs(r6) == current match count, then save record of current tag
------ if r6 is negative AND match has already been found,
------ then throw DUPE_LIMIT_ERROR
if no matches were found, throw NO_MATCH_ERROR
When returning a match, if the tag pointer is blank, the tag address is returned in r3.
Otherwise, the allocation pointed to by the tag is returned in r3.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID
r8 = total duplicates counted
r9 = dictionary ID
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
r6 = duplicate instance count/limit
for each tag in this player’s dictionary:
if the tag matches r4,
-- if the size matches r5; OR if r5 == 0
---- if r6 == 0, then return match
---- else, if abs(r6) == current match count, then save record of current tag
------ if r6 is negative AND match has already been found,
------ then throw DUPE_LIMIT_ERROR
if no matches were found, throw NO_MATCH_ERROR
When returning a match, if the tag pointer is blank, the tag address is returned in r3.
Otherwise, the allocation pointed to by the tag is returned in r3.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID
r8 = total duplicates counted
r9 = dictionary ID
<pdtSafeSeek>
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a Seek operation with a strict limit on the number of duplicate instance matches allowed in results.
If SafeSeek finds more than one instance of the specified symbol, it will return DUPE_LIMIT_ERROR in place of a symbol.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID
r8 = total duplicates counted
r9 = dictionary ID
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a Seek operation with a strict limit on the number of duplicate instance matches allowed in results.
If SafeSeek finds more than one instance of the specified symbol, it will return DUPE_LIMIT_ERROR in place of a symbol.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID
r8 = total duplicates counted
r9 = dictionary ID
<pdtQuickSeek>
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a Seek operation without accounting for duplicate matches.
Returns after first matching instance is found -- making the scan faster than a complete Seek operation.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID
r8 = total duplicates counted
r9 = dictionary ID
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a Seek operation without accounting for duplicate matches.
Returns after first matching instance is found -- making the scan faster than a complete Seek operation.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID
r8 = total duplicates counted
r9 = dictionary ID
<pdtSeekPush>
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
r6 = duplicate instance count/limit
Perform a Seek operation.
If the Seek operation returns a NO_MATCHES_ERROR then perform a Push operation.
After push has been performed, set returned r7 to Null 0 to signify a push was made.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID or null for push
r8 = total duplicates counted
r9 = dictionary ID
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
r6 = duplicate instance count/limit
Perform a Seek operation.
If the Seek operation returns a NO_MATCHES_ERROR then perform a Push operation.
After push has been performed, set returned r7 to Null 0 to signify a push was made.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID or null for push
r8 = total duplicates counted
r9 = dictionary ID
<pdtSafeSeekPush>
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a SeekPush operation, but with SafeSeek parameters.
If Seek operation returns DUPE_LIMIT_ERROR in place of a NO_MATCHES_ERROR -- then no push is made.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID or null for push
r8 = total duplicates counted
r9 = dictionary ID
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a SeekPush operation, but with SafeSeek parameters.
If Seek operation returns DUPE_LIMIT_ERROR in place of a NO_MATCHES_ERROR -- then no push is made.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID or null for push
r8 = total duplicates counted
r9 = dictionary ID
<pdtQuickSeekPush>
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a SeekPush operation, but with QuickSeek parameters.
If Seek operation finds any matches, it will return the result immediately and forgo pushing.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID or null for push
r8 = total duplicates counted
r9 = dictionary ID
d
input:
r3 = internal player data start address
r4 = 32-bit tag
r5 = allocation size (or 0)
Perform a SeekPush operation, but with QuickSeek parameters.
If Seek operation finds any matches, it will return the result immediately and forgo pushing.
output:
r3 = returned address, or null for error
r4 = 32-bit tag
r5 = allocation size
r6 = player data offset, or error code
r7 = matching duplicate instance ID or null for push
r8 = total duplicates counted
r9 = dictionary ID
d
Last edited: