| 
									
										
										
										
											2023-04-11 00:18:33 -07:00
										 |  |  | import bsdiff4 | 
					
						
							| 
									
										
										
										
											2023-03-21 01:26:03 +09:00
										 |  |  | import binascii | 
					
						
							| 
									
										
										
										
											2023-03-31 05:05:51 -07:00
										 |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2023-03-21 01:26:03 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | b2h = binascii.hexlify | 
					
						
							|  |  |  | h2b = binascii.unhexlify | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ROM: | 
					
						
							| 
									
										
										
										
											2023-04-11 00:18:33 -07:00
										 |  |  |     def __init__(self, filename, patches=None): | 
					
						
							| 
									
										
										
										
											2023-04-06 11:06:34 -07:00
										 |  |  |         data = open(Utils.user_path(filename), "rb").read() | 
					
						
							| 
									
										
										
										
											2023-04-11 00:18:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if patches: | 
					
						
							|  |  |  |             for patch in patches: | 
					
						
							|  |  |  |                 data = bsdiff4.patch(data, patch) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-21 01:26:03 +09:00
										 |  |  |         self.banks = [] | 
					
						
							|  |  |  |         for n in range(0x40): | 
					
						
							|  |  |  |             self.banks.append(bytearray(data[n*0x4000:(n+1)*0x4000])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def patch(self, bank_nr, addr, old, new, *, fill_nop=False): | 
					
						
							|  |  |  |         new = h2b(new) | 
					
						
							|  |  |  |         bank = self.banks[bank_nr] | 
					
						
							|  |  |  |         if old is not None: | 
					
						
							|  |  |  |             if isinstance(old, int): | 
					
						
							|  |  |  |                 old = bank[addr:old] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 old = h2b(old) | 
					
						
							|  |  |  |             if fill_nop: | 
					
						
							|  |  |  |                 assert len(old) >= len(new), "Length mismatch: %d != %d (%s != %s)" % (len(old), len(new), b2h(old), b2h(new)) | 
					
						
							|  |  |  |                 new += b'\x00' * (len(old) - len(new)) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 assert len(old) == len(new), "Length mismatch: %d != %d (%s != %s)" % (len(old), len(new), b2h(old), b2h(new)) | 
					
						
							|  |  |  |             assert addr >= 0 and addr + len(old) <= 16*1024 | 
					
						
							|  |  |  |             if bank[addr:addr+len(old)] != old: | 
					
						
							|  |  |  |                 if bank[addr:addr + len(old)] == new: | 
					
						
							|  |  |  |                     # Patch is already applied. | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 loc = bank.find(old) | 
					
						
							|  |  |  |                 while loc > -1: | 
					
						
							|  |  |  |                     print("Possible at:", hex(loc)) | 
					
						
							|  |  |  |                     loc = bank.find(old, loc+1) | 
					
						
							|  |  |  |                 assert False, "Patch mismatch:\n%s !=\n%s at 0x%04x" % (b2h(bank[addr:addr+len(old)]), b2h(old), addr) | 
					
						
							|  |  |  |         bank[addr:addr+len(new)] = new | 
					
						
							|  |  |  |         assert len(bank) == 0x4000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fixHeader(self, *, name=None): | 
					
						
							|  |  |  |         if name is not None: | 
					
						
							|  |  |  |             name = name.encode("utf-8") | 
					
						
							|  |  |  |             name = (name + (b"\x00" * 15))[:15] | 
					
						
							|  |  |  |             self.banks[0][0x134:0x143] = name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         checksum = 0 | 
					
						
							|  |  |  |         for c in self.banks[0][0x134:0x14D]: | 
					
						
							|  |  |  |             checksum -= c + 1 | 
					
						
							|  |  |  |         self.banks[0][0x14D] = checksum & 0xFF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # zero out the checksum before calculating it. | 
					
						
							|  |  |  |         self.banks[0][0x14E] = 0 | 
					
						
							|  |  |  |         self.banks[0][0x14F] = 0 | 
					
						
							|  |  |  |         checksum = 0 | 
					
						
							|  |  |  |         for bank in self.banks: | 
					
						
							|  |  |  |             checksum = (checksum + sum(bank)) & 0xFFFF | 
					
						
							|  |  |  |         self.banks[0][0x14E] = checksum >> 8 | 
					
						
							|  |  |  |         self.banks[0][0x14F] = checksum & 0xFF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def save(self, file, *, name=None): | 
					
						
							|  |  |  |         # don't pass the name to fixHeader | 
					
						
							|  |  |  |         self.fixHeader() | 
					
						
							|  |  |  |         if isinstance(file, str): | 
					
						
							|  |  |  |             f = open(file, "wb") | 
					
						
							|  |  |  |             for bank in self.banks: | 
					
						
							|  |  |  |                 f.write(bank) | 
					
						
							|  |  |  |             f.close() | 
					
						
							|  |  |  |             print("Saved:", file) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             for bank in self.banks: | 
					
						
							|  |  |  |                 file.write(bank) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def readHexSeed(self): | 
					
						
							|  |  |  |         return self.banks[0x3E][0x2F00:0x2F10].hex().upper() |