| 
									
										
										
										
											2021-11-07 15:38:02 +01:00
										 |  |  | import bsdiff4 | 
					
						
							|  |  |  | import yaml | 
					
						
							|  |  |  | from typing import Optional | 
					
						
							|  |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  | from Patch import APDeltaPatch | 
					
						
							| 
									
										
										
										
											2022-03-31 05:08:15 +02:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2021-11-07 15:38:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-10 09:17:27 +01:00
										 |  |  | USHASH = '6e9c94511d04fac6e0a1e582c170be3a' | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SoEDeltaPatch(APDeltaPatch): | 
					
						
							|  |  |  |     hash = USHASH | 
					
						
							|  |  |  |     game = "Secret of Evermore" | 
					
						
							|  |  |  |     patch_file_ending = ".apsoe" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def get_source_data(cls) -> bytes: | 
					
						
							|  |  |  |         with open(get_base_rom_path(), "rb") as stream: | 
					
						
							|  |  |  |             return read_rom(stream) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-31 05:08:15 +02:00
										 |  |  | def get_base_rom_path(file_name: Optional[str] = None) -> str: | 
					
						
							|  |  |  |     options = Utils.get_options() | 
					
						
							|  |  |  |     if not file_name: | 
					
						
							|  |  |  |         file_name = options["soe_options"]["rom_file"] | 
					
						
							|  |  |  |     if not os.path.exists(file_name): | 
					
						
							|  |  |  |         file_name = Utils.user_path(file_name) | 
					
						
							|  |  |  |     return file_name | 
					
						
							| 
									
										
										
										
											2021-11-10 09:17:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 15:38:02 +01:00
										 |  |  | def read_rom(stream, strip_header=True) -> bytes: | 
					
						
							|  |  |  |     """Reads rom into bytearray and optionally strips off any smc header""" | 
					
						
							|  |  |  |     data = stream.read() | 
					
						
							|  |  |  |     if strip_header and len(data) % 0x400 == 0x200: | 
					
						
							|  |  |  |         return data[0x200:] | 
					
						
							|  |  |  |     return data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_yaml(patch: bytes, metadata: Optional[dict] = None) -> bytes: | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  |     """Generate old (<4) apbp format yaml""" | 
					
						
							| 
									
										
										
										
											2021-11-07 15:38:02 +01:00
										 |  |  |     patch = yaml.dump({"meta": metadata, | 
					
						
							|  |  |  |                        "patch": patch, | 
					
						
							|  |  |  |                        "game": "Secret of Evermore", | 
					
						
							|  |  |  |                        # minimum version of patch system expected for patching to be successful | 
					
						
							|  |  |  |                        "compatible_version": 1, | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  |                        "version": 2, | 
					
						
							| 
									
										
										
										
											2021-11-10 09:17:27 +01:00
										 |  |  |                        "base_checksum": USHASH}) | 
					
						
							| 
									
										
										
										
											2021-11-07 15:38:02 +01:00
										 |  |  |     return patch.encode(encoding="utf-8-sig") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_patch(vanilla_file, randomized_file, metadata: Optional[dict] = None) -> bytes: | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  |     """Generate old (<4) apbp format patch data. Run through lzma to get a complete apbp file.""" | 
					
						
							| 
									
										
										
										
											2021-11-07 15:38:02 +01:00
										 |  |  |     with open(vanilla_file, "rb") as f: | 
					
						
							|  |  |  |         vanilla = read_rom(f) | 
					
						
							|  |  |  |     with open(randomized_file, "rb") as f: | 
					
						
							|  |  |  |         randomized = read_rom(f) | 
					
						
							|  |  |  |     if metadata is None: | 
					
						
							|  |  |  |         metadata = {} | 
					
						
							|  |  |  |     patch = bsdiff4.diff(vanilla, randomized) | 
					
						
							|  |  |  |     return generate_yaml(patch, metadata) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  |     import sys | 
					
						
							|  |  |  |     print('Please use ../../Patch.py', file=sys.stderr) | 
					
						
							|  |  |  |     sys.exit(1) |