#include #include "functions.h" #include "variables.h" #include "SnS.h" #include "save.h" bool snsToRestoreItems = FALSE; struct SnsPayload *snsBasePayloadPtr1 = NULL; struct SnsPayload *snsBasePayloadPtr2 = NULL; struct SnsPayload *snsBasePayloadPtr3 = NULL; struct SnsPayload *snsBasePayloadPtr4 = NULL; /* .bss */ StopNSwop_Data snsParsedKeys; struct GlobalSave gSaveData; s32 snsMinKeyToParse; s32 snsMaxKeyToParse; s32 snsParsedCurrPos; u32 snsBackedUpItems; void sns_init_parsing_params(s32 min, s32 max) { snsMinKeyToParse = min; snsMaxKeyToParse = max; snsParsedCurrPos = 0; } u32 sns_get_next_key_in_range(void) { while (snsParsedCurrPos < SNS_PAYLOAD_DATALEN) { if (snsParsedKeys[snsParsedCurrPos] >= snsMinKeyToParse && snsParsedKeys[snsParsedCurrPos] <= snsMaxKeyToParse) return snsParsedKeys[snsParsedCurrPos++]; snsParsedCurrPos++; } return 0; } void sns_update_global_save_data_checksum(void) { s32 i; /** * Updates the save data checksum multiple times (?) * * Running this multiple times seems to achieve nothing. * A debug leftover? */ for (i = 5; i != 0 && savedata_8033CE40(&gSaveData); i--) ; } void sns_save_and_update_global_data(void) { s32 i; /** * Triggers a save of the global save data to EEPROM, * and validates the checksum. */ if (savedata_8033CA9C(&gSaveData)) { /** * Failed, hide the evidence. * * Clears the entire global data area. */ gSaveData.sns.uEggYellow = FALSE; gSaveData.sns.uEggRed = FALSE; gSaveData.sns.uEggGreen = FALSE; gSaveData.sns.uEggBlue = FALSE; gSaveData.sns.uEggPink = FALSE; gSaveData.sns.uEggCyan = FALSE; gSaveData.sns.uIceKey = FALSE; gSaveData.sns.cEggYellow = FALSE; gSaveData.sns.cEggRed = FALSE; gSaveData.sns.cEggGreen = FALSE; gSaveData.sns.cEggBlue = FALSE; gSaveData.sns.cEggPink = FALSE; gSaveData.sns.cEggCyan = FALSE; gSaveData.sns.cIceKey = FALSE; gSaveData.snsw = gSaveData.snsw << SNS_NUM_FLAGS >> SNS_NUM_FLAGS ^ gSaveData.snsw; for (i = 0; i < sizeof(gSaveData.UNUSED); i++) gSaveData.UNUSED[i] = 0; sns_update_global_save_data_checksum(); } sns_unlock_parsed_items(); sns_update_global_save_data_checksum(); } /** * Scans N64 RDRAM for a valid payload, and if found, * parses it for the key values and saves them internally. */ void sns_find_and_parse_payload(void) { struct SnsPayload *payload; s32 i; for (i = 0; i < SNS_PAYLOAD_DATALEN; i++) snsParsedKeys[i] = 0; payload = snspayload_find_payload_in_ram(); if (payload) { snspayload_rewind_incoming(); i = 0; while (i < SNS_PAYLOAD_DATALEN) { u32 val = snspayload_get_next_key(payload); snsParsedKeys[i] = val; if (val) i++; else i = SNS_PAYLOAD_DATALEN; } } } void sns_init_base_payloads(void) { snsBasePayloadPtr3 = snspayload_init_new_payload((struct SnsPayload *)0x803FFF00); snsBasePayloadPtr4 = snspayload_init_new_payload((struct SnsPayload *)0x803A5C00); snsBasePayloadPtr1 = snspayload_init_new_payload((struct SnsPayload *)func_8025484C(0x100)); snsBasePayloadPtr2 = snspayload_init_new_payload((struct SnsPayload *)func_80254898(0x100)); } bool sns_get_or_set_key(bool state, struct SnsPayload *payload, s32 key, s32 mode) { if (mode == SNS_MODE_WRITE) { if (state) snspayload_append_key_to_outgoing_payload(payload, key); return state; } if (mode == SNS_MODE_READ) { /** * A VERY roundabout way of simply searching through the * parsed values to see if the given key is present. * * It feels like a leftover remnant from code written * hastily during testing of SnS. */ sns_init_parsing_params(key, key); return sns_get_next_key_in_range() ? TRUE : state; } return FALSE; } void sns_commit_collected_flags(s32 mode, struct SnsPayload *payload) { gSaveData.sns.cEggYellow = sns_get_or_set_key(gSaveData.sns.cEggYellow, payload, SNS_ITEM_COLLECTED_EggYellow, mode); gSaveData.sns.cEggRed = sns_get_or_set_key(gSaveData.sns.cEggRed, payload, SNS_ITEM_COLLECTED_EggRed, mode); gSaveData.sns.cEggGreen = sns_get_or_set_key(gSaveData.sns.cEggGreen, payload, SNS_ITEM_COLLECTED_EggGreen, mode); gSaveData.sns.cEggBlue = sns_get_or_set_key(gSaveData.sns.cEggBlue, payload, SNS_ITEM_COLLECTED_EggBlue, mode); gSaveData.sns.cEggPink = sns_get_or_set_key(gSaveData.sns.cEggPink, payload, SNS_ITEM_COLLECTED_EggPink, mode); gSaveData.sns.cEggCyan = sns_get_or_set_key(gSaveData.sns.cEggCyan, payload, SNS_ITEM_COLLECTED_EggCyan, mode); gSaveData.sns.cIceKey = sns_get_or_set_key(gSaveData.sns.cIceKey, payload, SNS_ITEM_COLLECTED_IceKey, mode); } void sns_commit_unlocked_flags(s32 mode, struct SnsPayload *payload) { gSaveData.sns.uEggYellow = sns_get_or_set_key(gSaveData.sns.uEggYellow, payload, SNS_ITEM_UNLOCKED_EggYellow, mode); gSaveData.sns.uEggRed = sns_get_or_set_key(gSaveData.sns.uEggRed, payload, SNS_ITEM_UNLOCKED_EggRed, mode); gSaveData.sns.uEggGreen = sns_get_or_set_key(gSaveData.sns.uEggGreen, payload, SNS_ITEM_UNLOCKED_EggGreen, mode); gSaveData.sns.uEggBlue = sns_get_or_set_key(gSaveData.sns.uEggBlue, payload, SNS_ITEM_UNLOCKED_EggBlue, mode); gSaveData.sns.uEggPink = sns_get_or_set_key(gSaveData.sns.uEggPink, payload, SNS_ITEM_UNLOCKED_EggPink, mode); gSaveData.sns.uEggCyan = sns_get_or_set_key(gSaveData.sns.uEggCyan, payload, SNS_ITEM_UNLOCKED_EggCyan, mode); gSaveData.sns.uIceKey = sns_get_or_set_key(gSaveData.sns.uIceKey, payload, SNS_ITEM_UNLOCKED_IceKey, mode); } void sns_unlock_parsed_items(void) { sns_commit_unlocked_flags(SNS_MODE_READ, NULL); } void sns_generate_payload(struct SnsPayload *payload) { snspayload_rewind_outgoing(); snspayload_append_key_to_outgoing_payload(payload, 1); sns_commit_collected_flags(SNS_MODE_WRITE, payload); snspayload_finalise_outgoing_payload(payload); } /** * Writes payload to RAM for another Rareware game * to read and parse after swapping cartridges. */ void sns_write_payload_over_heap(void) { s32 val2; u32 i; if (globalTimer_getTime() <= 0x3B || snsToRestoreItems) return; sns_generate_payload(snsBasePayloadPtr2); wmemcpy(snsBasePayloadPtr3, snsBasePayloadPtr2, sizeof(*snsBasePayloadPtr2)); wmemcpy(snsBasePayloadPtr4, snsBasePayloadPtr2, sizeof(*snsBasePayloadPtr2)); for (i = 1; i < 5; i++) { u32 val1 = func_80254BD0(&val2, i); if (val1 && val2 > 0x4000) { s32 val3 = val1 - 0x400 + val2; val1 -= val1 & 0x1FFF; for (val1 += 0x2C00; val1 < val3; val1 += 0x2000) wmemcpy((void *)val1, snsBasePayloadPtr2, sizeof(*snsBasePayloadPtr2)); } } } void sns_stub(void) {} /** * Dev flag: to boot straight to a specific map. * * This value is unused in the retail builds, but may have * been used in debug builds. * * If TRUE, on boot, the game will start in the furnace fun * portal room (used for testing furnace fun?) * * If FALSE, on boot, the game will start on the file select * screen (skips all the logo cutscenes) */ bool DEBUG_use_special_bootmap(void) { return FALSE; } /** * Dev flag: purpose unknown? */ bool func_8025B818(void) { return TRUE; } bool sns_get_item_state(enum StopNSwop_Item item, s32 set) { switch (item) { case SNS_ITEM_EGG_YELLOW: return set == SNS_COLLECTED ? gSaveData.sns.cEggYellow : gSaveData.sns.uEggYellow; case SNS_ITEM_EGG_RED: return set == SNS_COLLECTED ? gSaveData.sns.cEggRed : gSaveData.sns.uEggRed; case SNS_ITEM_EGG_GREEN: return set == SNS_COLLECTED ? gSaveData.sns.cEggGreen : gSaveData.sns.uEggGreen; case SNS_ITEM_EGG_BLUE: return set == SNS_COLLECTED ? gSaveData.sns.cEggBlue : gSaveData.sns.uEggBlue; case SNS_ITEM_EGG_PINK: return set == SNS_COLLECTED ? gSaveData.sns.cEggPink : gSaveData.sns.uEggPink; case SNS_ITEM_EGG_CYAN: return set == SNS_COLLECTED ? gSaveData.sns.cEggCyan : gSaveData.sns.uEggCyan; case SNS_ITEM_ICE_KEY: return set == SNS_COLLECTED ? gSaveData.sns.cIceKey : gSaveData.sns.uIceKey; default: return FALSE; } } void sns_set_item_state(enum StopNSwop_Item item, s32 set, s32 state) { if (set == SNS_COLLECTED) { switch (item) { case SNS_ITEM_EGG_YELLOW: gSaveData.sns.cEggYellow = state; return; case SNS_ITEM_EGG_RED: gSaveData.sns.cEggRed = state; return; case SNS_ITEM_EGG_GREEN: gSaveData.sns.cEggGreen = state; return; case SNS_ITEM_EGG_BLUE: gSaveData.sns.cEggBlue = state; return; case SNS_ITEM_EGG_PINK: gSaveData.sns.cEggPink = state; return; case SNS_ITEM_EGG_CYAN: gSaveData.sns.cEggCyan = state; return; // debug string? case SNS_ITEM_ICE_KEY: gSaveData.sns.cIceKey = state; if (0); return; default: return; } } else // SNS_UNLOCKED { switch (item) { case SNS_ITEM_EGG_YELLOW: gSaveData.sns.uEggYellow = state; return; case SNS_ITEM_EGG_RED: gSaveData.sns.uEggRed = state; return; case SNS_ITEM_EGG_GREEN: gSaveData.sns.uEggGreen = state; return; case SNS_ITEM_EGG_BLUE: gSaveData.sns.uEggBlue = state; return; case SNS_ITEM_EGG_PINK: gSaveData.sns.uEggPink = state; return; case SNS_ITEM_EGG_CYAN: gSaveData.sns.uEggCyan = state; return; case SNS_ITEM_ICE_KEY: gSaveData.sns.uIceKey = state; return; default: return; } } } void sns_set_item_and_update_payload(enum StopNSwop_Item item, s32 set, s32 state) { sns_set_item_state(item, set, state); sns_update_global_save_data_checksum(); sns_write_payload_over_heap(); } /** * Used for the cutscene at the end of the game where Mumbo shows * you Stop 'n' Swop hints. * * Backs up your item flags, then forces them all to be unlocked * and not collected, so they can be seen during the hint sequences. */ void sns_backup_items_and_unlock_all(void) { u32 i; if (snsToRestoreItems) return; sns_write_payload_over_heap(); snsToRestoreItems = TRUE; // statements on same line for match (why?) snsBackedUpItems = 0; for (i = 1; i < SNS_ITEM_length; i++) { snsBackedUpItems = (sns_get_item_state(i, SNS_UNLOCKED) ? 1 : 0) + (snsBackedUpItems << 1); snsBackedUpItems = (sns_get_item_state(i, SNS_COLLECTED) ? 1 : 0) + (snsBackedUpItems << 1); sns_set_item_state(i, SNS_UNLOCKED, TRUE); sns_set_item_state(i, SNS_COLLECTED, FALSE); } } /** * Used when ending the cutscene where Mumbo shows you Stop 'n' * Swop hints. * * It restores whatever item flags you had before it was triggered. */ void sns_restore_backed_up_items(void) { u32 i; if (!snsToRestoreItems) return; for (i = SNS_ITEM_length - 1; i > 0; i--) { sns_set_item_state(i, SNS_COLLECTED, snsBackedUpItems & 1); snsBackedUpItems >>= 1; sns_set_item_state(i, SNS_UNLOCKED, snsBackedUpItems & 1); snsBackedUpItems >>= 1; } snsToRestoreItems = FALSE; }