/// <summary> /// This method is called when attempting to remove a param value that once pointed to another location (Pointer type) that MIGHT be an external subroutine call /// <para>If it is determined to be a call to an external subroutine, the external subroutine data table is updated to no longer pointer to it</para> /// </summary> /// <param name="removedPsaCommand">The psa command being removed</param> /// <param name="commandParamPointerValueLocation">The pointer to the param's value location</param> private void UpdateExternalPointerLogic(PsaCommand removedPsaCommand, int commandParamPointerValueLocation) { // iterate through each external subroutine in the external data table for (int externalSubRoutineIndex = 0; externalSubRoutineIndex < PsaFile.NumberOfExternalSubRoutines; externalSubRoutineIndex++) { // get a external subroutine's pointer int externalSubRoutineLocationIndex = (PsaFile.NumberOfDataTableEntries + externalSubRoutineIndex) * 2; int externalSubRoutineLocation = PsaFile.DataTableSections[externalSubRoutineLocationIndex]; if (externalSubRoutineLocation >= 8096 && externalSubRoutineLocation < PsaFile.DataSectionSize) { // if the param being removed was pointing to an external subroutine if (commandParamPointerValueLocation == externalSubRoutineLocation) { // get the location of the value of the param int commandExternalSubroutineValueLocation = commandParamPointerValueLocation / 4; // This stops the external data table from pointing to the command param pointer value location if (PsaFile.DataSection[commandExternalSubroutineValueLocation] >= 8096 && PsaFile.DataSection[commandExternalSubroutineValueLocation] < PsaFile.DataSectionSize && PsaFile.DataSection[commandExternalSubroutineValueLocation] % 4 == 0) { PsaFile.DataTableSections[externalSubRoutineLocationIndex] = PsaFile.DataSection[commandExternalSubroutineValueLocation]; } else { PsaFile.DataTableSections[externalSubRoutineLocationIndex] = -1; } break; } // same deal as above for the most part, but I am NOT sure what location "externalSubRoutineCodeBlockLocation" actually is (I don't think it's right currently) // it's tough to test further because I also can't really figure out how to get this code to trigger at the moment, so I'm just going to leave this as is int externalSubRoutineCodeBlockLocation = externalSubRoutineLocation / 4; int externalSubRoutineCommandsPointerLocation = PsaFile.DataSection[externalSubRoutineCodeBlockLocation]; if (externalSubRoutineCommandsPointerLocation >= 8096 && externalSubRoutineCommandsPointerLocation < PsaFile.DataSectionSize && removedPsaCommand.CommandParametersLocation == externalSubRoutineCommandsPointerLocation) { int commandExternalDataPointerValue = removedPsaCommand.GetCommandParameterValueLocation(0); if (PsaFile.DataSection[commandExternalDataPointerValue] >= 8096 && PsaFile.DataSection[commandExternalDataPointerValue] < PsaFile.DataSectionSize && PsaFile.DataSection[commandExternalDataPointerValue] % 4 == 0) { PsaFile.DataTableSections[externalSubRoutineCodeBlockLocation] = PsaFile.DataSection[commandExternalDataPointerValue]; } else { PsaFile.DataTableSections[externalSubRoutineCodeBlockLocation] = -1; } break; } } } }
/// <summary> /// Modify a command with existing parameters /// <para>Case 1: both old and new command have same number of parameters, which is an easy params replace 1 by 1</para> /// <para>Case 2: new command has less params than old command, which is the same as case 1, but the unused param space needs to be converted to free space (FADEF00D)</para> /// <para>Case 3: new command has more params than old command, which will result in finding a new location for the parameters that has enough space</para> /// </summary> /// <param name="commandLocation">The location of the command in the code block</param> /// <param name="oldPsaCommand">The old psa command (the one that is being modified)</param> /// <param name="newPsaCommand">The new psa command (the one that is replcaing the old psa command)</param> private void ModifyExistingCommandParametersLocation(int commandLocation, PsaCommand oldPsaCommand, PsaCommand newPsaCommand) { // If modifying a command that pointed to another location (the "Pointer" param type), the pointer location needs to be removed from the offset interlock tracker // If the pointer param was an external subroutine (from the external data table) (such as Mario's Up B, the item ones like the home run bat, etc), // some additional work needs to be done to remove references to it from the external data table // This if statement checks if the first parameter is of type Pointer or the second, which matches commands like "goto", "subroutine", and "concurrent subroutine" if (PsaFile.DataSection[oldPsaCommand.CommandParametersValuesLocation] == 2 || (PsaFile.DataSection[oldPsaCommand.CommandParametersValuesLocation + 2] == 2 && oldPsaCommand.NumberOfParams == 2)) { // Get the pointer location of the param value that is currently pointing to another location int commandParamPointerValueLocation = PsaFile.DataSection[oldPsaCommand.CommandParametersValuesLocation] == 2 ? oldPsaCommand.CommandParametersLocation + 4 : oldPsaCommand.CommandParametersLocation + 12; // Attempt to remove the above offset from the offset interlock tracker bool wasOffsetRemoved = PsaFileHelperMethods.RemoveOffsetFromOffsetInterlockTracker(commandParamPointerValueLocation); // If offset was not successfully removed, it means it either doesn't exist or is an external subroutine // The below method call UpdateExternalPointerLogic will do some necessary work if the offset turns out to be an external subroutine call if (!wasOffsetRemoved) { UpdateExternalPointerLogic(oldPsaCommand, commandParamPointerValueLocation); } } // Replace old param values with free space (FADEF00D) for (int paramIndex = 0; paramIndex < oldPsaCommand.NumberOfParams; paramIndex++) { int commandParameterTypeLocation = oldPsaCommand.GetCommandParameterTypeLocation(paramIndex); int commandParameterValueLocation = oldPsaCommand.GetCommandParameterValueLocation(paramIndex); PsaFile.DataSection[commandParameterTypeLocation] = Constants.FADEF00D; PsaFile.DataSection[commandParameterValueLocation] = Constants.FADEF00D; } // set new command instruction PsaFile.DataSection[commandLocation] = newPsaCommand.Instruction; // if new command has no parameters, set pointer to parameters to nothing (which is 0) and remove the pointer to the parameters from the offset interlock if (newPsaCommand.NumberOfParams == 0) { // remove pointer to params since this command has no params PsaFile.DataSection[commandLocation + 1] = 0; // remove offset from interlock tracker since it no longer exists int commandParametersPointerLocation = commandLocation * 4 + 4; PsaFileHelperMethods.RemoveOffsetFromOffsetInterlockTracker(commandParametersPointerLocation); } // if new command has parameters else { // if new command has a less or equal parameter count to the old command, the parameter values location does not need to be relocated // if the new command has a higher parameter count than the old command, the paramter values location will need to be expanded/relocated to have enough room for all the parameters int newCommandParametersValuesLocation = oldPsaCommand.NumberOfParams >= newPsaCommand.NumberOfParams ? oldPsaCommand.CommandParametersValuesLocation : ExpandCommandParametersSection(oldPsaCommand, newPsaCommand); PsaFile.DataSection[commandLocation + 1] = newCommandParametersValuesLocation * 4; // put param values in new param values location one by one for (int paramIndex = 0; paramIndex < newPsaCommand.NumberOfParams; paramIndex++) { int paramTypeLocation = paramIndex * 2; int paramValueLocation = paramIndex * 2 + 1; // if command param type is Pointer and it actually points to something if (newPsaCommand.Parameters[paramIndex].Type == 2 && newPsaCommand.Parameters[paramIndex].Value > 0) { // I believe this points to the location of the param value (if the param is a pointer) int commandParameterPointerValueLocation = (newCommandParametersValuesLocation + paramTypeLocation) * 4 + 4; PsaFile.OffsetSection.Add(commandParameterPointerValueLocation); } // place parameter type in value in proper place PsaFileHelperMethods.SetDataSectionValue(newCommandParametersValuesLocation + paramTypeLocation, newPsaCommand.Parameters[paramIndex].Type); PsaFileHelperMethods.SetDataSectionValue(newCommandParametersValuesLocation + paramValueLocation, newPsaCommand.Parameters[paramIndex].Value); } } }