void FreeSpaceListing::giveSpace( int address, int length) { // Check to see if we can add this space to an existing entry for (FreeSpaceList::iterator it = freeSpaceList_.begin(); it != freeSpaceList_.end(); it++) { // Ensure that we don't cross bank boundaries // (needs more thorough checking) if ((it->address() % LoadedROM::bankSize)) { // If this space comes exactly before an existing one, // modify the existing space with the new amount if (it->address() == address + length) { it->setAddress(address); it->setLength(it->length() + length); return; } // If this space comes exactly after an existing one, // modify the existing space with the new amount // as long as this would not cross a bank boundary else if ((address % LoadedROM::bankSize) && (it->address() + it->length() == address)) { it->setLength(it->length() + length); return; } } } // No existing space found: add new entry freeSpaceList_.push_back(FreeSpaceListPair(address, length)); }
FreeSpaceList::iterator FreeSpaceListing::getFreeSpace(int length) { FreeSpaceList::iterator it = freeSpaceList_.begin(); // Iterate over free space list until end is reached // or a sufficiently large space is found while ((it != freeSpaceList_.end()) && (it->length() < length)) { it++; } // Return result, or freeSpaceList_.end() on failure return it; }
void FreeSpaceListing::claimSpace( FreeSpaceList::iterator& position, int length) { // Throw if trying to claim more space than exists if (length > position->length()) { throw TGenericException(TALES_SRCANDLINE, "FreeSpaceListing::claimSpace(" "FreeSpaceList::iterator,int", std::string("Tried to claim ") + StringConversion::toString(length) + std::string(" bytes at position ") + StringConversion::toString(position->address()) + std::string(" when only ") + StringConversion::toString(position->length()) + std::string(" were available")); } // Increase address position->setAddress(position->address() + length); // Decrease length position->setLength(position->length() - length); // If all space at this address is claimed, remove it from the list if (position->length() == 0) { freeSpaceList_.erase(position); } }
FreeSpaceList::iterator FreeSpaceListing::getFreeSpace( int length, int low, int high) { FreeSpaceList::iterator it = freeSpaceList_.begin(); while ((it != freeSpaceList_.end())) { if (it->address() >= high) { break; } if ((it->address() >= low) && (it->address() < high) && (it->length() >= length)) { return it; } it++; } return freeSpaceList_.end(); }
void MetatileStructureSet::exportToROM(WritableROM& rom) { // TODO: support for adding metatile definitions // search freespace &c FreeSpaceList::iterator spaceIt = rom.freeSpace().getFreeSpace( exportSize()); if (spaceIt == rom.freeSpace().freeSpaceList().end()) { throw NotEnoughSpaceException(TALES_SRCANDLINE, "MetatileStructureSet::exportToROM(" "WritableROM&)", exportSize()); } int writeAddress = spaceIt->address(); address_ = spaceIt->address(); rom.freeSpace().claimSpace(spaceIt, exportSize()); // std::cout << "addr: " << writeAddress << std::endl; // Starting address of structure definitions int contentStartAddress = writeAddress + (index_.size() * ByteSizes::uint16Size); // Write the index for (MetatileIndexToStructureMap::iterator it = index_.begin(); it != index_.end(); it++) { int contentIndex = it->second; // Compute the address of the content Taddress contentAddress = (contentStartAddress + (contentIndex * MetatileStructure::dataSize)); // Convert to banked address Taddress contentBankedAddress = LoadedROM ::directToBankedAddress(contentAddress); // Write to index Tbyte buffer[ByteSizes::uint16Size]; ByteConversion::toBytes(contentBankedAddress, buffer, ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); rom.directWrite(writeAddress + (it->first * ByteSizes::uint16Size), buffer, ByteSizes::uint16Size); } // Write content int metatileNum = 0; for (MetatileStructureCollection::iterator it = primaryStorage_.begin(); it != primaryStorage_.end(); it++) { // Write metatile to buffer Tbyte buffer[MetatileStructure::dataSize]; it->writeToData(buffer); // Write buffer to table rom.directWrite(contentStartAddress + (metatileNum * MetatileStructure::dataSize), buffer, MetatileStructure::dataSize); ++metatileNum; } // std::cout << "metatiles: " << metatileNum << std::endl; // std::cout << std::endl; }
Taddress EditableLevelObjectEntryGroups::moveToNewBank(WritableROM& rom) { // std::cout << "Preparing to move to new bank" << std::endl; // More for convenience than anything else, we grab a whole bank of data // for use by this table (and a few bits of associated code). // Note that this basically requires expanding the ROM to 1 MB FreeSpaceList::iterator spaceIt = rom.freeSpace().getFreeSpace(LoadedROM::bankSize); // Throw if a full bank isn't available if (spaceIt == rom.freeSpace().freeSpaceList().end()) { throw NotEnoughSpaceException(TALES_SRCANDLINE, "EditableLevelObjectEntryGroups::" "exportToROM(WritableROM&)", LoadedROM::bankSize); } // std::cout << spaceIt->address() << " " << spaceIt->length() << std::endl; Taddress newBaseAddress = spaceIt->address(); // Claim the bank rom.freeSpace().claimSpace(spaceIt, LoadedROM::bankSize); // std::cout << "New base address: " << newBaseAddress << std::endl; // Copy code segments C1 and C3 to new bank. // C1 and C3 both contain code that references the object table, // so they must (to avoid more complicated hacking) be in the same bank. // After rearranging the code, the new bank will contain C1, C3, and // the object table, in that order, starting from the beginning of the // bank // Copy C1 to start of bank Taddress newC1Address = newBaseAddress; std::memcpy(rom.directWrite(newC1Address), codeSegmentC1, lengthOfC1Segment); // Copy C3 to directly after C1 Taddress newC3Address = newC1Address + lengthOfC1Segment; std::memcpy(rom.directWrite(newC3Address), codeSegmentC3, lengthOfC3Segment); // Set new export address for table (directly after C3) int exportAddress = newBaseAddress + lengthOfC1Segment + lengthOfC3Segment; // std::cout << "New C1 address: " << newC1Address << std::endl; // std::cout << "New C3 address: " << newC3Address << std::endl; // std::cout << "Export address: " << exportAddress << std::endl; // Update all the code that references the old locations of C1 and C3 // Get the banked address for the new direct address of C1 int newC1BankNum = LoadedROM::directToBankNum(newC1Address); int newC1BankedAddress = LoadedROM::directToBankedAddress(newC1Address); // Get the banked address for the new direct address of C3 int newC3BankNum = LoadedROM::directToBankNum(newC3Address); int newC3BankedAddress = LoadedROM::directToBankedAddress(newC3Address); // Update bank number in code that calls C1 ByteConversion::toBytes(newC1BankNum, rom.directWrite(callReferenceToC1Bank), ByteSizes::uint8Size, EndiannessTypes::little, SignednessTypes::nosign); // C1's banked address is the same (0000), so we don't need to update it // ... but do anyway for consistency ByteConversion::toBytes(newC1BankedAddress, rom.directWrite(callReferenceToC1Address), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Update banked address in code that calls C3 ByteConversion::toBytes(newC3BankNum, rom.directWrite(callReferenceToC3Bank), ByteSizes::uint8Size, EndiannessTypes::little, SignednessTypes::nosign); // Update bank number in code that calls C3. ByteConversion::toBytes(newC3BankedAddress, rom.directWrite(callReferenceToC3Address), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // C3 contains a hardcoded JP instruction into itself. Since we moved // C3, we have to update the JP to correspond to the new location. // Get new address of the JP instruction's address parameter Taddress addressOfNewJpParameter = newC3Address + offsetOfJpInC3; // Get new address of the JP instruction's jump point base Taddress addressOfNewJpBase = newC3Address + baseOffsetOfJpInC3; // Compute the new target address Taddress newJpTargetAddress = addressOfNewJpBase - absoluteLengthOfJpInC3; // std::cout << "New JP target: " << newJpTargetAddress << std::endl; // Convert to banked form Taddress newJpTargetBankedAddress = LoadedROM::directToBankedAddress(newJpTargetAddress); // Write the new target address to the JP parameter ByteConversion::toBytes(newJpTargetBankedAddress, rom.directWrite(addressOfNewJpParameter), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Get the banked address for the new direct address of the object table // int newTableBankNum // = LoadedROM::directToBankNum(exportAddress); int newTableBankedAddress = LoadedROM::directToBankedAddress(exportAddress); // Update C1 to refer to the new location of the object table ByteConversion::toBytes(newTableBankedAddress, rom.directWrite(newC1Address + offsetOfTableReferenceInC1), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Update C3 to refer to the new location of the object table ByteConversion::toBytes(newTableBankedAddress, rom.directWrite(newC3Address + offsetOfTableReferenceInC3), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Update table information // initialTableAddress_ = exportAddress; // initialTableContentSize_ = spaceIt->length() // - exportAddress // - tableHeaderSize_; // initialTableContentSize_ = exportAddress - spaceIt->address() // - tableHeaderSize_; // initialTableContentSize_ = LoadedROM::bankSize; // std::cout << initialTableContentSize_ << std::endl; // Mark that we've moved to a new bank movedToNewBank_ = true; return exportAddress; }
void TailsAdvBank0Hacks::addUseAllInventoryHack( WritableROM& rom) { // Find free space FreeSpaceList::iterator spaceIt = rom.freeSpace().getFreeSpace(allInventoryHackCodeDataSize); // Throw if not enough space if (spaceIt == rom.freeSpace().freeSpaceList().end()) { throw NotEnoughSpaceException(TALES_SRCANDLINE, "TailsAdvBank0Hacks::addUseAllInventoryHack(" "WritableROM&)", allInventoryHackCodeDataSize); } Taddress writeAddress = spaceIt->address(); rom.freeSpace().claimSpace(spaceIt, allInventoryHackCodeDataSize); // std::cout << writeAddress << std::endl; // Write main code and data rom.directWrite(writeAddress, allInventoryHackMainData, allInventoryHackMainLength); // Fill in addresses of data tables ByteConversion::toBytes(LoadedROM::directToBankedAddress(writeAddress + allInventoryHackMainTable1Offset), rom.directWrite(writeAddress + allInventoryHackMainTable1ReferenceOffset), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); ByteConversion::toBytes(LoadedROM::directToBankedAddress(writeAddress + allInventoryHackMainTable2Offset), rom.directWrite(writeAddress + allInventoryHackMainTable2ReferenceOffset), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Add trigger 1 rom.directWrite(allInventoryHackTrigger1Address, allInventoryHackTrigger1Data, allInventoryHackTrigger1Length); // Fill in bank ByteConversion::toBytes(LoadedROM::directToBankNum(writeAddress + allInventoryHackMainCodeStartOffset), rom.directWrite( allInventoryHackTrigger1Address + allInventoryHackTrigger1BankReferenceOffset), ByteSizes::uint8Size, EndiannessTypes::little, SignednessTypes::nosign); // Fill in address ByteConversion::toBytes(LoadedROM::directToBankedAddress(writeAddress + allInventoryHackMainCodeStartOffset), rom.directWrite( allInventoryHackTrigger1Address + allInventoryHackTrigger1AddressReferenceOffset), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Add trigger 2 rom.directWrite(allInventoryHackTrigger2Address, allInventoryHackTrigger2Data, allInventoryHackTrigger2Length); // Fill in bank ByteConversion::toBytes(LoadedROM::directToBankNum(writeAddress + allInventoryHackMainCodeStartOffset), rom.directWrite( allInventoryHackTrigger2Address + allInventoryHackTrigger2BankReferenceOffset), ByteSizes::uint8Size, EndiannessTypes::little, SignednessTypes::nosign); // Fill in address ByteConversion::toBytes(LoadedROM::directToBankedAddress(writeAddress + allInventoryHackMainCodeStartOffset), rom.directWrite( allInventoryHackTrigger2Address + allInventoryHackTrigger2AddressReferenceOffset), ByteSizes::uint16Size, EndiannessTypes::little, SignednessTypes::nosign); // Add initializer 1 rom.directWrite(allInventoryHackInitializer1Address, allInventoryHackInitializer1Data, allInventoryHackInitializer1Length); // Add initializer 2 rom.directWrite(allInventoryHackInitializer2Address, allInventoryHackInitializer2Data, allInventoryHackInitializer2Length); // Add initializer 3 rom.directWrite(allInventoryHackInitializer3Address, allInventoryHackInitializer3Data, allInventoryHackInitializer3Length); // Add initializer 4 rom.directWrite(allInventoryHackInitializer4Address, allInventoryHackInitializer4Data, allInventoryHackInitializer4Length); }