| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | import base64 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-08 16:52:34 -04:00
										 |  |  | from ..rom.ips import IPS_Patch | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | def pc_to_snes(pcaddress): | 
					
						
							|  |  |  |     snesaddress=(((pcaddress<<1)&0x7F0000)|(pcaddress&0x7FFF)|0x8000)|0x800000 | 
					
						
							|  |  |  |     return snesaddress | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def snes_to_pc(B): | 
					
						
							|  |  |  |     B_1 = B >> 16 | 
					
						
							|  |  |  |     B_2 = B & 0xFFFF | 
					
						
							|  |  |  |     # return 0 if invalid LoROM address | 
					
						
							|  |  |  |     if B_1 < 0x80 or B_1 > 0xFFFFFF or B_2 < 0x8000: | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  |     A_1 = (B_1 - 0x80) >> 1 | 
					
						
							|  |  |  |     # if B_1 is even, remove most significant bit | 
					
						
							|  |  |  |     A_2 = B_2 & 0x7FFF if (B_1 & 1) == 0 else B_2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return (A_1 << 16) | A_2 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  | VANILLA_ROM_SIZE = 3145728 | 
					
						
							|  |  |  | BANK_SIZE = 0x8000 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | class ROM(object): | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def __init__(self, data={}): | 
					
						
							|  |  |  |         self.address = 0 | 
					
						
							|  |  |  |         self.maxAddress = VANILLA_ROM_SIZE | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def seek(self, address): | 
					
						
							|  |  |  |         if address > self.maxAddress: | 
					
						
							|  |  |  |             self.maxAddress = address | 
					
						
							|  |  |  |         self.address = address | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def tell(self): | 
					
						
							|  |  |  |         if self.address > self.maxAddress: | 
					
						
							|  |  |  |             self.maxAddress = self.address | 
					
						
							|  |  |  |         return self.address | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def inc(self, n=1): | 
					
						
							|  |  |  |         self.address += n | 
					
						
							|  |  |  |         self.tell() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def read(self, byteCount): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |     def readWord(self, address=None): | 
					
						
							|  |  |  |         return self.readBytes(2, address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def readByte(self, address=None): | 
					
						
							|  |  |  |         return self.readBytes(1, address) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def readLong(self, address=None): | 
					
						
							|  |  |  |         return self.readBytes(3, address) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |     def readBytes(self, size, address=None): | 
					
						
							|  |  |  |         if address != None: | 
					
						
							|  |  |  |             self.seek(address) | 
					
						
							|  |  |  |         return int.from_bytes(self.read(size), byteorder='little') | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |      | 
					
						
							|  |  |  |     def write(self, bytes): | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def writeWord(self, word, address=None): | 
					
						
							|  |  |  |         self.writeBytes(word, 2, address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def writeByte(self, byte, address=None): | 
					
						
							|  |  |  |         self.writeBytes(byte, 1, address) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def writeLong(self, lng, address=None): | 
					
						
							|  |  |  |         self.writeBytes(lng, 3, address) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |     def writeBytes(self, value, size, address=None): | 
					
						
							|  |  |  |         if address != None: | 
					
						
							|  |  |  |             self.seek(address) | 
					
						
							|  |  |  |         self.write(value.to_bytes(size, byteorder='little')) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def ipsPatch(self, ipsPatches): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fillToNextBank(self): | 
					
						
							|  |  |  |         off = self.maxAddress % BANK_SIZE | 
					
						
							|  |  |  |         if off > 0: | 
					
						
							|  |  |  |             self.seek(self.maxAddress + BANK_SIZE - off - 1) | 
					
						
							|  |  |  |             self.writeByte(0xff) | 
					
						
							|  |  |  |         assert (self.maxAddress % BANK_SIZE) == 0 | 
					
						
							| 
									
										
										
										
											2025-01-10 15:46:17 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | class FakeROM(ROM): | 
					
						
							|  |  |  |     # to have the same code for real ROM and the webservice | 
					
						
							|  |  |  |     def __init__(self, data={}): | 
					
						
							|  |  |  |         super(FakeROM, self).__init__() | 
					
						
							|  |  |  |         self.data = data | 
					
						
							|  |  |  |         self.ipsPatches = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write(self, bytes): | 
					
						
							|  |  |  |         for byte in bytes: | 
					
						
							|  |  |  |             self.data[self.address] = byte | 
					
						
							|  |  |  |             self.inc() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def read(self, byteCount): | 
					
						
							|  |  |  |         bytes = [] | 
					
						
							|  |  |  |         for i in range(byteCount): | 
					
						
							|  |  |  |             bytes.append(self.data[self.address]) | 
					
						
							|  |  |  |             self.inc() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return bytes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def ipsPatch(self, ipsPatches): | 
					
						
							|  |  |  |         self.ipsPatches += ipsPatches | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # generate ips from self data | 
					
						
							|  |  |  |     def ips(self): | 
					
						
							|  |  |  |         groupedData = {} | 
					
						
							|  |  |  |         startAddress = -1 | 
					
						
							|  |  |  |         prevAddress = -1 | 
					
						
							|  |  |  |         curData = [] | 
					
						
							|  |  |  |         for address in sorted(self.data): | 
					
						
							|  |  |  |             if address == prevAddress + 1: | 
					
						
							|  |  |  |                 curData.append(self.data[address]) | 
					
						
							|  |  |  |                 prevAddress = address | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if len(curData) > 0: | 
					
						
							|  |  |  |                     groupedData[startAddress] = curData | 
					
						
							|  |  |  |                 startAddress = address | 
					
						
							|  |  |  |                 prevAddress = address | 
					
						
							|  |  |  |                 curData = [self.data[startAddress]] | 
					
						
							|  |  |  |         if startAddress != -1: | 
					
						
							|  |  |  |             groupedData[startAddress] = curData | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return IPS_Patch(groupedData) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # generate final IPS for web patching with first the IPS patches, then written data | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         self.mergedIPS = IPS_Patch() | 
					
						
							|  |  |  |         for ips in self.ipsPatches: | 
					
						
							|  |  |  |             self.mergedIPS.append(ips) | 
					
						
							|  |  |  |         self.mergedIPS.append(self.ips()) | 
					
						
							|  |  |  |         #patchData = mergedIPS.encode() | 
					
						
							|  |  |  |         #self.data = {} | 
					
						
							|  |  |  |         #self.data["ips"] = base64.b64encode(patchData).decode() | 
					
						
							|  |  |  |         #if mergedIPS.truncate_length is not None: | 
					
						
							|  |  |  |         #    self.data["truncate_length"] = mergedIPS.truncate_length | 
					
						
							|  |  |  |         #self.data["max_size"] = mergedIPS.max_size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getPatchDict(self): | 
					
						
							|  |  |  |         return self.mergedIPS.toDict() | 
					
						
							|  |  |  |                  | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | class RealROM(ROM): | 
					
						
							|  |  |  |     def __init__(self, name): | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         super(RealROM, self).__init__() | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         self.romFile = open(name, "rb+") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def seek(self, address): | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         super(RealROM, self).seek(address) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         self.romFile.seek(address) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def tell(self): | 
					
						
							|  |  |  |         self.address = self.romFile.tell() | 
					
						
							|  |  |  |         return super(RealROM, self).tell() | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |     def write(self, bytes): | 
					
						
							|  |  |  |         self.romFile.write(bytes) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         self.tell() | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def read(self, byteCount): | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         ret = self.romFile.read(byteCount) | 
					
						
							|  |  |  |         self.tell() | 
					
						
							|  |  |  |         return ret | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         self.romFile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def ipsPatch(self, ipsPatches): | 
					
						
							|  |  |  |         for ips in ipsPatches: | 
					
						
							|  |  |  |             ips.applyFile(self) |