Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- SerialExchangeBytes: sends and receives a number of bytes (send from hl, receive in de)
- the sent data is typically no longer useful after being sent and the game will often reuse the buffer afterwards
- when a buffer with leading preamble bytes is sent, the game accounts for the possibility of receiving a different
- number of preamble bytes (which in fact does happen at times), so receive buffers never have fixed addresses/offsets
- and are always used as staging areas
- Patch sets:
- The link hardware has no way of sending "nothing". While the link is idle, the game sends $FE (SERIAL_NO_DATA_BYTE).
- Therefore, actual data cannot contain any $FE bytes. Since the original data can definitely contain those values, the
- game "patches" the data that will be sent to avoid this. The format of a patch set is simple: an $FF-terminated list
- of offsets where the data originally contained $FE bytes; those bytes themselves are replaced by $FF when the data is
- sent. (Both the terminator $FF and the replacement $FF are identified as SERIAL_PATCH_LIST_PART_TERMINATOR, but those
- two values are in fact unrelated.)
- Patch sets cannot contain any offset larger than $FC, and thus the game will concatenate multiple patch sets. Oddly,
- the offsets in patch sets are 1-based, and address computations account for this. (For example, the first patch set is
- for the buffer starting at wLinkData + 25, and if this address had to be patched, the patch set would contain $01.)
- FixDataForLinkTransfer: will scan a buffer for $FE bytes and replace them with dummy $FF while creating a list of
- patches for the receiving side to apply to the received data. This creates a buffer in $c508 (wc508, one of those
- buffers used for everything).
- This function also prepares the random number data to be exchanged, for some reason.
- Patch preparation process:
- 1) initialize the patch buffer (at wc508) with 3 preamble bytes
- 2) fill the subsequent 200 bytes with zeros (this is larger than the patch sets could ever be)
- 3) hl = (wLinkData + SERIAL_PREAMBLE_LENGTH + NAME_LENGTH + PARTY_LENGTH + 2) - 1, de = wc508 + 10 (3 preamble, 7 ???)
- note: this makes hl points to the mon data structs. Notably, the player name is skipped. What happens if the player
- name somehow has an 8 ($FE) in it?
- 4) loop for $FC bytes and create the first patch set; terminate it with $FF
- (note that b is used as a loop limit (the loop will iterate b-1 times), but b = 0 for this first loop, so the loop
- will exit early due to c >= $FD)
- if $FC is defined as a constant (say, PATCH_SET_LENGTH), then cp SERIAL_PREAMBLE_BYTE right after .loop2 should be
- changed to cp PATCH_SET_LENGTH + 1
- 5) b = $D (for gen 1) or $27 (for gen 2), making a total of $108/$122 bytes to patch (the loop runs b-1 times)
- notes:
- for gen 1, $108 = PARTY_LENGTH * REDMON_STRUCT_LENGTH
- for gen 2, $122 = 2 (for the player ID) + PARTY_LENGTH * PARTYMON_STRUCT_LENGTH
- the value of b can be calculated as (limit - $FC) + 1
- 6) loop again and make a second patch set, terminating it with $FF (this time b will limit the loop)
- note: hl hasn't been updated, so this patch set continues from $FC bytes after the first patch set
- When creating patch sets, this function will replace the patched bytes with $FF. This value is just a replacement,
- which is ignored by the receiving side (as it will immediately overwrite these bytes with $FE); the replacement value
- is incorrectly identified as SERIAL_PATCH_LIST_PART_TERMINATOR, but it is in fact independent from the terminator.
- Gen2ToGen1LinkComms:
- 0) Link_PrepPartyData_Gen1 + FixDataForLinkTransfer: stage party data to be sent in wLinkData and wc508
- 1) exchange random numbers
- 2) exchange party data
- $1a8 = SERIAL_PREAMBLE_LENGTH (preamble) + NAME_LENGTH (player name) +
- PARTY_LENGTH + 2 (party count, data, terminator) + PARTY_LENGTH * REDMON_STRUCT_LENGTH (party data) +
- PARTY_LENGTH * NAME_LENGTH * 2 (OT names, nicknames) + 3 (not used, probably just in case the other side
- sends too many preamble bytes)
- data received in OT data (wOTPlayerName onwards); this is just staging and it doesn't have fixed addresses because
- the number of preamble bytes received may vary
- 3) exchange patch sets (200 bytes at wc508); there's no particular meaning to the number 200 other than that patch
- sets should never be this long (send in wc508, receive in wTrademons -- used as a temporary buffer)
- 4) copy back random numbers and set hl = wOTPlayerName (receive buffer for party data) advanced to the first real data
- byte (i.e., not control, not zero)
- 5) test the received party count byte and exit with a link error if the party count isn't between 1 and 6 (cp 7 should
- be cp PARTY_LENGTH + 1)
- 6) de = wLinkData, bc = $1a2 (same constant as step 2 minus SERIAL_PREAMBLE_LENGTH), copy received data from hl (which
- points to the temporary buffer at wOTPlayerName + whatever offset to the beginning of the real data) to wLinkData
- 7) de = wPlayerTrademon (= wTrademons from step 3), hl = wTradeCapsulePlayerData (= wLinkData + NAME_LENGTH +
- PARTY_LENGTH + 2, i.e., the offset from step 3 in FixDataForLinkTransfer without the serial preamble), and apply
- the first patch set
- 8) hl = wc80f (= previous hl + $FC, see FixDataForLinkTransfer step 6), apply the second patch set
- 9) convert received gen 1 data to gen 2 format (loading it into wOTxxxxx) and finish up (opening the trade menu)
- Gen2ToGen2LinkComms:
- 0, 1) same as gen 1 (calling Link_PrepPartyData_Gen2 instead -- which will also handle mail data; see below)
- 2) same as gen 1, exchange $1c2 bytes
- $1c2 = SERIAL_PREAMBLE_LENGTH + NAME_LENGTH + (PARTY_LENGTH + 2) + 2 (player ID) +
- (PARTY_LENGTH * PARTYMON_STRUCT_LENGTH) + (PARTY_LENGTH * NAME_LENGTH * 2) + 3 (not used)
- 3) same as gen 1
- 4) if the open link is a trade, exchange mail data (390 bytes, send from wc8f4, receive in wca84; see the description
- of Link_PrepPartyData_Gen2 below for the layout of this data)
- 5) same as gen 1 step 4
- 6, 7, 8) same as gen 1 (copied amount is $1b9, which is $1c2 - SERIAL_PREAMBLE_LENGTH - the 3 at the end)
- 9) if the open link is a trade, handle the mail data: (otherwise, jump to .skip_mail)
- - hl = wca84, then skip the preamble bytes (custom preamble, $20, incorrectly identified as MAIL_MSG_LENGTH here)
- - again, skip custom preamble (incorrect: MAIL_MSG_LENGTH) bytes and $FE (SERIAL_NO_DATA_BYTE) bytes (unclear why
- this is done twice? Probably GF being GF... this could be a single loop)
- - copy 400 bytes (!!! 10 more than the exchanged bytes in step 4) to wca84, reusing the same buffer (it is
- interesting to note that wca84 - wc8f4 = 400; perhaps they just took the size of that block?)
- note: this is copying from wca84 to *itself*, with the source pointer incremented by a few bytes (after having
- skipped the preamble); this relies on the fact that CopyBytes does a forwards copy. To my knowledge, this is the
- only time CopyBytes is called with overlapping buffer pointers
- - iterate through the next 198 (PARTY_LENGTH * (MAIL_MSG_LENGTH + 1)) bytes, replacing $21 bytes with $FE (this is
- undoing step 3 of Link_PrepPartyData_Gen2; the $21 is incorrectly identified as MAIL_MSG_LENGTH + 1 here)
- - de = wcb9e (= wca84 + PARTY_LENGTH * MAIL_STRUCT_LENGTH, pointing at the patch set in the received data), and
- apply the patch set based on wcb4a (= wca84 + PARTY_LENGTH * (MAIL_MSG_LENGTH + 1), pointing at the mail headers)
- - copy the mail data into wc8f4 (reusing the send buffer), reassembling the mail structs by interleaving the bodies
- and the headers (bodies are copied first, leaving space for the headers; headers are copied afterwards)
- - write a zero byte at wca0e (= wc8f4 + PARTY_LENGTH * MAIL_STRUCT_LENGTH), after the six received mail structs
- 10) load all of the received trainer data into wOTxxxxx
- 11) if the link is a battle, set up the battle initial variables and fire it up
- 12) otherwise, finish up and pop up the trade menu
- Exchanged data (gen 1): three blocks of data:
- Random numbers: 7 bytes preamble, 10 bytes data
- Trainer data:
- Preamble: 6 bytes of $FD (SERIAL_PREAMBLE_LENGTH, SERIAL_PREAMBLE_BYTE)
- Name: 11 (NAME_LENGTH)
- Party list: 8 (count, 6 species, terminator, pad with $00)
- Party data: 6 * 44 (REDMON_STRUCT_LENGTH), just the party struct with $FE (SERIAL_NO_DATA_BYTE) bytes replaced
- OT names: 6 * 11
- Nicknames: 6 * 11
- Dummy filler: 3 (probably padding the length of the buffer in case more than 6 preamble bytes are received)
- Patch sets:
- Preamble: 3 bytes of $FD
- Dummy: 7 bytes of $00 (don't seem to be particularly useful)
- Patch set 1 (variable length, for the first $FC bytes starting at the party data)
- Patch set 2 (variable length, for the remaining $C bytes)
- $00 padding up to 200 bytes
- Link_PrepPartyData_Gen1 fills in the trainer data in a pretty straightforward way; ConvertMon_2to1 is called to
- transform the party structs into gen 1 structs
- Exchanged data (gen 2): four blocks of data:
- Random numbers: 7 bytes preamble, 10 bytes data
- Trainer data:
- Preamble: 6 bytes of $FD (SERIAL_PREAMBLE_LENGTH, SERIAL_PREAMBLE_BYTE)
- Name: 11 (NAME_LENGTH)
- Party list: 8 (count, 6 species, terminator, pad with $00)
- Player ID: 2 bytes ($FE bytes replaced)
- Party data: 6 * 48 (PARTYMON_STRUCT_LENGTH) with $FE (SERIAL_NO_DATA_BYTE) bytes replaced
- OT names: 6 * 11
- Nicknames: 6 * 11
- Dummy filler: 3 (probably padding the length of the buffer in case more than 6 preamble bytes are received)
- Patch sets:
- Preamble: 3 bytes of $FD
- Dummy: 7 bytes of $00 (don't seem to be particularly useful)
- Patch set 1 (variable length, for the first $FC bytes starting at the player ID)
- Patch set 2 (variable length, for the remaining $26 bytes of party data)
- $00 padding up to 200 bytes
- Mail (sent only if trading, not battling):
- Preamble: 5 bytes of $20 (custom preamble probably because $FD is a valid mail data byte)
- Mail bodies: 6 * 33 (MAIL_MSG_LENGTH + 1, includes the terminator), with $FE bytes replaced with $21
- Mail headers: 6 * 14 (size of the rest of the mail struct), with $FE bytes replaced with $FF
- Patch set (variable length) for the mail headers
- $00 padding up to 390 bytes
- Link_PrepPartyData_Gen2 fills in the trainer data in an unremarkable way. If the open link is a trade, it also loads
- the mail data into wc8f4:
- 1) write five bytes of $20 (custom preamble) to wc8f4
- note: for the rest of this description, assume SERIAL_MAIL_PREAMBLE_LENGTH = 5 (new constant)
- 2) copy the mail bodies, then the mail headers
- 3) hl = wc8f4 + SERIAL_MAIL_PREAMBLE_LENGTH, iterate through the mail bodies and replace all $FE (SERIAL_NO_DATA_BYTE)
- bytes with $21 (a custom replacement byte, incorrectly identified as sPartyMon1MailAuthor - sPartyMon1Mail right
- above .skip2)
- 4) hl = wc8f4 + SERIAL_MAIL_PREAMBLE_LENGTH + PARTY_LENGTH * (MAIL_MSG_LENGTH + 1), pointing at the mail headers
- (redundant, since it will already exit the previous loop at this address), de = pointing to the patch sets
- (= the address above + PARTY_LENGTH * (sPartyMon1MailEnd - sPartyMon1MailAuthor)), b = length of mail headers
- 5) manually build a patch set for the b bytes at hl, write at de (as all patch sets, the offsets are 1-based), while
- replacing the patched bytes with $FF (incorrectly identified as SERIAL_PATCH_LIST_PART_TERMINATOR; see a similar
- note in FixDataForLinkTransfer)
- 6) write the patch set terminator and exit
- The mail data process uses several unnamed labels which are simply offsets into the mail data block:
- - wc8f4: beginning
- - wc8f9: mail bodies, wc8f4 + SERIAL_MAIL_PREAMBLE_LENGTH
- - wc9bf: mail headers, previous + PARTY_LENGTH * (MAIL_MSG_LENGTH + 1)
- - wca13: patch set, previous + PARTY_LENGTH * (sPartyMon1MailEnd - sPartyMon1MailAuthor)
- The game will exchange 390 bytes at wc8f4, so this area technically ends at $ca7a. This leaves 103 bytes for the patch
- set; it's naturally never even close to that long, but the block is large just in case.
- ***
- These notes only cover the first half of link.asm. The code starting at InitTradeMenuDisplay is generally well
- understood and doesn't need as much fixing as the code that actually exchanges data when a link is opened.
- ***
- Buffers used by the exchange:
- The initial exchange uses several buffers, sometimes reusing them as the exchange progresses. The following steps can
- be seen in the code:
- 1) To-be-sent data staging
- 2) Received data staging (exchange the staged data with the received data)
- 3) Data fixing
- Notably, while the first two steps cannot use overlapping buffers, the rest of the code can and will; the location
- where the received data will be finally deposited after being fixed is also used as a staging area. This requires
- treating the whole link data area as a union.
- Link data is located mainly in a very large buffer that starts at $c700 (wLinkData) and ends at $cc14 (wLinkDataEnd).
- This is a 1300-byte buffer -- this value is not at all a coincidence, as the memory region spans exactly the same
- WRAM area as the overworld map buffer. Many sub-regions from this block are used at various points throughout the link
- code to hold data; other parts are unused, but the whole 1300-byte block is nonetheless cleared (set to zero) by the
- ClearLinkData function before any data staging or exchange takes place.
- After all the transfers are done, the only part of this region that will contain persistent meaningful data is the
- buffer at wc8f4 during gen 2 to gen 2 trades, which contains six mailmsg structs followed by a single zero byte
- (probably a terminator). During battles or Time Capsule trades, there will be no data left over in wLinkData.
- The link code also uses a generic buffer at $c508 to stage patch sets to be sent. This is a multipurpose buffer used
- by many parts of the code, right after the tilemap; the link code only uses 200 bytes of that buffer, although it is
- in fact 480 bytes long.
- Data staging before sending:
- wLinkBattleRNPreamble, wLinkBattleRNs: self-explanatory
- wLinkData: trainer data, with different layouts for gen 1 and gen 2
- wc8f4: mail data (see the layout in the Link_PrepPartyData_Gen2 description)
- wc508: patch sets for trainer data
- Received data staging:
- (note that the received data has no explicit layout because the game assumes that the number of preamble/no data bytes
- at the beginning may vary. This actually does happen in practice, and thus any structure given to these buffers is
- meaningless, although the structure after the initial dummy bytes is obviously identical to the sent data structure.)
- wEnemyMon: the received random numbers; the game (correctly) assumes that this buffer is not in use here
- OT variables (wOTPlayerName through wOTPartyDataEnd): received trainer data
- wTrademons: received patch sets for trainer data
- wca84: received mail data
- Data fixing:
- (this is where received data is temporarily copied for fixing before loading into its final destination; patch sets
- (except for the one in the mail data) are never copied since they are applied directly to the data fixed here, and
- random numbers are not copied since they don't need any fixing)
- wLinkData: trainer data, with the same layout as the sent data (different for gen 1 and gen 2) but without the initial
- preamble bytes (and, for gen 2, without the trailing dummy bytes)
- wca84: mail data, with the same layout as the sent data, but without the initial preamble bytes -- this data is copied
- onto itself, but shifted slightly to get rid of the leading preamble bytes
- Final destination for received data:
- wLinkBattleRNs: self-explanatory
- OT variables: trainer and party data
- wc8f4: mail data (six mailmsg structs plus a terminator byte); this is the only part of the data that remains in the
- wLinkData block
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement