mirror of
https://github.com/a2-4am/passport.py.git
synced 2024-06-16 11:29:29 +00:00
more patchers
This commit is contained in:
parent
e3b9a097e2
commit
c65b66c441
|
@ -12,4 +12,4 @@ def opener(filename):
|
||||||
return wozimage.EDDReader(filename)
|
return wozimage.EDDReader(filename)
|
||||||
raise RuntimeError("unrecognized file type")
|
raise RuntimeError("unrecognized file type")
|
||||||
|
|
||||||
EDDToWoz(opener(sys.argv[1]), DefaultLogger)
|
Crack(opener(sys.argv[1]), DefaultLogger)
|
||||||
|
|
|
@ -356,6 +356,7 @@ class BasePassportProcessor: # base class
|
||||||
self.g = PassportGlobals()
|
self.g = PassportGlobals()
|
||||||
self.g.disk_image = disk_image
|
self.g.disk_image = disk_image
|
||||||
self.logger = logger_class(self.g)
|
self.logger = logger_class(self.g)
|
||||||
|
self.rwts = None
|
||||||
self.output_tracks = {}
|
self.output_tracks = {}
|
||||||
self.patchers = []
|
self.patchers = []
|
||||||
self.patches_found = []
|
self.patches_found = []
|
||||||
|
@ -367,7 +368,7 @@ class BasePassportProcessor: # base class
|
||||||
#JMPBECAPatcher,
|
#JMPBECAPatcher,
|
||||||
#JMPB660Patcher,
|
#JMPB660Patcher,
|
||||||
#JMPB720Patcher,
|
#JMPB720Patcher,
|
||||||
#BadEmuPatcher,
|
bademu.BadEmuPatcher,
|
||||||
#BadEmu2Patcher,
|
#BadEmu2Patcher,
|
||||||
rwts.RWTSPatcher,
|
rwts.RWTSPatcher,
|
||||||
#RWTSLogPatcher,
|
#RWTSLogPatcher,
|
||||||
|
@ -399,8 +400,8 @@ class BasePassportProcessor: # base class
|
||||||
#T11DiskVolPatcher,
|
#T11DiskVolPatcher,
|
||||||
#T02VolumeNamePatcher,
|
#T02VolumeNamePatcher,
|
||||||
universale7.UniversalE7Patcher,
|
universale7.UniversalE7Patcher,
|
||||||
#A6BC95Patcher,
|
a6bc95.A6BC95Patcher,
|
||||||
#A5CountPatcher,
|
a5count.A5CountPatcher,
|
||||||
d5d5f7.D5D5F7Patcher,
|
d5d5f7.D5D5F7Patcher,
|
||||||
#ProDOSRWTSPatcher,
|
#ProDOSRWTSPatcher,
|
||||||
#ProDOS6APatcher,
|
#ProDOS6APatcher,
|
||||||
|
@ -421,7 +422,7 @@ class BasePassportProcessor: # base class
|
||||||
#BootCounterPatcher,
|
#BootCounterPatcher,
|
||||||
#JMPB412Patcher,
|
#JMPB412Patcher,
|
||||||
#JMPB400Patcher,
|
#JMPB400Patcher,
|
||||||
#AdvIntPatcher,
|
advint.AdventureInternationalPatcher,
|
||||||
#JSR8635Patcher,
|
#JSR8635Patcher,
|
||||||
#JMPB4BBPatcher,
|
#JMPB4BBPatcher,
|
||||||
#DOS32MUSEPatcher,
|
#DOS32MUSEPatcher,
|
||||||
|
@ -436,13 +437,13 @@ class BasePassportProcessor: # base class
|
||||||
if self.run():
|
if self.run():
|
||||||
self.postprocess()
|
self.postprocess()
|
||||||
|
|
||||||
def SkipTrack(self, rwts, track_num, track):
|
def SkipTrack(self, track_num, track):
|
||||||
# don't look for whole-track protections on track 0, that's silly
|
# don't look for whole-track protections on track 0, that's silly
|
||||||
if track_num == 0: return False
|
if track_num == 0: return False
|
||||||
# Electronic Arts protection track?
|
# Electronic Arts protection track?
|
||||||
if track_num == 6:
|
if track_num == 6:
|
||||||
if rwts.find_address_prologue(track):
|
if self.rwts.find_address_prologue(track):
|
||||||
address_field = rwts.address_field_at_point(track)
|
address_field = self.rwts.address_field_at_point(track)
|
||||||
if address_field and address_field.track_num == 5: return True
|
if address_field and address_field.track_num == 5: return True
|
||||||
# Nibble count track?
|
# Nibble count track?
|
||||||
repeated_nibble_count = 0
|
repeated_nibble_count = 0
|
||||||
|
@ -464,9 +465,97 @@ class BasePassportProcessor: # base class
|
||||||
def IDDiversi(self, t00s00):
|
def IDDiversi(self, t00s00):
|
||||||
"""returns True if T00S00 is Diversi-DOS bootloader, or False otherwise"""
|
"""returns True if T00S00 is Diversi-DOS bootloader, or False otherwise"""
|
||||||
return find.at(0xF1, t00s00,
|
return find.at(0xF1, t00s00,
|
||||||
b'\xB3\xA3\xA0\xD2\xCF\xD2\xD2\xC5'
|
b'\xB3\xA3\xA0\xD2\xCF\xD2\xD2\xC5'
|
||||||
b'\x8D\x87\x8D')
|
b'\x8D\x87\x8D')
|
||||||
|
|
||||||
|
def IDProDOS(self, t00s00):
|
||||||
|
"""returns True if T00S00 is ProDOS bootloader, or False otherwise"""
|
||||||
|
return find.at(0x00, t00s00,
|
||||||
|
b'\x01'
|
||||||
|
b'\x38'
|
||||||
|
b'\xB0\x03'
|
||||||
|
b'\x4C')
|
||||||
|
|
||||||
|
def IDPascal(self, t00s00):
|
||||||
|
"""returns True if T00S00 is Pascal bootloader, or False otherwise"""
|
||||||
|
if find.wild_at(0x00, t00s00,
|
||||||
|
b'\x01'
|
||||||
|
b'\xE0\x60'
|
||||||
|
b'\xF0\x03'
|
||||||
|
b'\x4C' + find.WILDCARD + b'\x08'):
|
||||||
|
return True
|
||||||
|
return find.at(0x00, t00s00,
|
||||||
|
b'\x01'
|
||||||
|
b'\xE0\x70'
|
||||||
|
b'\xB0\x04'
|
||||||
|
b'\xE0\x40'
|
||||||
|
b'\xB0')
|
||||||
|
|
||||||
|
def IDDavidDOS(self, t00s00):
|
||||||
|
"""returns True if T00S00 is David-DOS II bootloader, or False otherwise"""
|
||||||
|
if not find.at(0x01, t00s00,
|
||||||
|
b'\xA5\x27'
|
||||||
|
b'\xC9\x09'
|
||||||
|
b'\xD0\x17'):
|
||||||
|
return False
|
||||||
|
return find.wild_at(0x4A, t00s00,
|
||||||
|
b'\xA2' + find.WILDCARD + \
|
||||||
|
b'\xBD' + find.WILDCARD + b'\x08' + \
|
||||||
|
b'\x9D' + find.WILDCARD + b'\x04' + \
|
||||||
|
b'\xCA'
|
||||||
|
b'\x10\xF7')
|
||||||
|
|
||||||
|
def IDDatasoft(self, t00s00):
|
||||||
|
"""returns True if T00S00 is encrypted Datasoft bootloader, or False otherwise"""
|
||||||
|
return find.at(0x00, t00s00,
|
||||||
|
b'\x01\x4C\x7E\x08\x04\x8A\x0C\xB8'
|
||||||
|
b'\x00\x56\x10\x7A\x00\x00\x1A\x16'
|
||||||
|
b'\x12\x0E\x0A\x06\x53\x18\x9A\x02'
|
||||||
|
b'\x10\x1B\x02\x10\x4D\x56\x15\x0B'
|
||||||
|
b'\xBF\x14\x14\x54\x54\x54\x92\x81'
|
||||||
|
b'\x1B\x10\x10\x41\x06\x73\x0A\x10'
|
||||||
|
b'\x33\x4E\x00\x73\x12\x10\x33\x7C'
|
||||||
|
b'\x00\x11\x20\xE3\x49\x50\x73\x1A'
|
||||||
|
b'\x10\x41\x00\x23\x80\x5B\x0A\x10'
|
||||||
|
b'\x0B\x4E\x9D\x0A\x10\x9D\x0C\x10'
|
||||||
|
b'\x60\x1E\x53\x10\x90\x53\xBC\x90'
|
||||||
|
b'\x53\x00\x90\xD8\x52\x00\xD8\x7C'
|
||||||
|
b'\x00\x53\x80\x0B\x06\x41\x00\x09'
|
||||||
|
b'\x04\x45\x0C\x63\x04\x90\x94\xD0'
|
||||||
|
b'\xD4\x23\x04\x91\xA1\xEB\xCD\x06'
|
||||||
|
b'\x95\xA1\xE1\x98\x97\x86')
|
||||||
|
|
||||||
|
def IDMicrograms(self, t00s00):
|
||||||
|
"""returns True if T00S00 is Micrograms bootloader, or False otherwise"""
|
||||||
|
if not find.at(0x01, t00s00,
|
||||||
|
b'\xA5\x27'
|
||||||
|
b'\xC9\x09'
|
||||||
|
b'\xD0\x12'
|
||||||
|
b'\xA9\xC6'
|
||||||
|
b'\x85\x3F'):
|
||||||
|
return False
|
||||||
|
return find.at(0x42, t00s00, b'\x4C\x00')
|
||||||
|
|
||||||
|
def IDQuickDOS(self, t00s00):
|
||||||
|
"""returns True if T00S00 is Quick-DOS bootloader, or False otherwise"""
|
||||||
|
return find.at(0x01, t00s00,
|
||||||
|
b'\xA5\x27'
|
||||||
|
b'\xC9\x09'
|
||||||
|
b'\xD0\x27'
|
||||||
|
b'\x78'
|
||||||
|
b'\xAD\x83\xC0')
|
||||||
|
|
||||||
|
def IDRDOS(self, t00s00):
|
||||||
|
"""returns True if T00S00 is Quick-DOS bootloader, or False otherwise"""
|
||||||
|
return find.at(0x00, t00s00,
|
||||||
|
b'\x01'
|
||||||
|
b'\xA9\x60'
|
||||||
|
b'\x8D\x01\x08'
|
||||||
|
b'\xA2\x00'
|
||||||
|
b'\xA0\x1F'
|
||||||
|
b'\xB9\x00\x08'
|
||||||
|
b'\x49')
|
||||||
|
|
||||||
def IDDOS33(self, t00s00):
|
def IDDOS33(self, t00s00):
|
||||||
"""returns True if T00S00 is DOS bootloader or some variation
|
"""returns True if T00S00 is DOS bootloader or some variation
|
||||||
that can be safely boot traced, or False otherwise"""
|
that can be safely boot traced, or False otherwise"""
|
||||||
|
@ -544,13 +633,13 @@ class BasePassportProcessor: # base class
|
||||||
|
|
||||||
def IDBootloader(self, t00):
|
def IDBootloader(self, t00):
|
||||||
"""returns RWTS object that can (hopefully) read the rest of the disk"""
|
"""returns RWTS object that can (hopefully) read the rest of the disk"""
|
||||||
rwts = UniversalRWTSIgnoreEpilogues(self.logger)
|
temporary_rwts_for_t00 = UniversalRWTSIgnoreEpilogues(self.logger)
|
||||||
physical_sectors = rwts.decode_track(t00)
|
physical_sectors = temporary_rwts_for_t00.decode_track(t00)
|
||||||
if 0 not in physical_sectors:
|
if 0 not in physical_sectors:
|
||||||
self.logger.PrintByID("fatal0000")
|
self.logger.PrintByID("fatal0000")
|
||||||
return None
|
return None
|
||||||
t00s00 = physical_sectors[0]
|
t00s00 = physical_sectors[0].decoded
|
||||||
|
|
||||||
if self.IDDOS33(t00s00):
|
if self.IDDOS33(t00s00):
|
||||||
self.g.is_boot0 = True
|
self.g.is_boot0 = True
|
||||||
if self.IDDiversi(t00s00):
|
if self.IDDiversi(t00s00):
|
||||||
|
@ -559,15 +648,30 @@ class BasePassportProcessor: # base class
|
||||||
self.logger.PrintByID("prontodos")
|
self.logger.PrintByID("prontodos")
|
||||||
else:
|
else:
|
||||||
self.logger.PrintByID("dos33boot0")
|
self.logger.PrintByID("dos33boot0")
|
||||||
# TODO handle JSR08B3 here
|
logical_sectors = temporary_rwts_for_t00.reorder_to_logical_sectors(physical_sectors)
|
||||||
rwts = self.TraceDOS33(rwts.reorder_to_logical_sectors(physical_sectors), rwts)
|
return self.TraceDOS33(logical_sectors)
|
||||||
else:
|
# TODO JSR08B3
|
||||||
self.logger.PrintByID("builtin")
|
# TODO MECC fastloader
|
||||||
self.g.tried_univ = True
|
# TODO DOS 3.3P
|
||||||
rwts = UniversalRWTS(self.logger)
|
# TODO Laureate
|
||||||
return rwts
|
# TODO Electronic Arts
|
||||||
|
# TODO DOS 3.2
|
||||||
|
# TODO IDEncoded44
|
||||||
|
# TODO IDEncoded53
|
||||||
|
self.g.is_prodos = self.IDProDOS(t00s00)
|
||||||
|
if self.g.is_prodos:
|
||||||
|
# TODO IDVolumeName
|
||||||
|
# TODO IDDinkeyDOS
|
||||||
|
pass
|
||||||
|
self.g.is_pascal = self.IDPascal(t00s00)
|
||||||
|
self.g.is_daviddos = self.IDDavidDOS(t00s00)
|
||||||
|
self.g.is_datasoft = self.IDDatasoft(t00s00)
|
||||||
|
self.g.is_micrograms = self.IDMicrograms(t00s00)
|
||||||
|
self.g.is_quickdos = self.IDQuickDOS(t00s00)
|
||||||
|
self.g.is_rdos = self.IDRDOS(t00s00)
|
||||||
|
return self.StartWithUniv()
|
||||||
|
|
||||||
def TraceDOS33(self, logical_sectors, rwts):
|
def TraceDOS33(self, logical_sectors):
|
||||||
"""returns RWTS object"""
|
"""returns RWTS object"""
|
||||||
|
|
||||||
use_builtin = False
|
use_builtin = False
|
||||||
|
@ -576,9 +680,7 @@ class BasePassportProcessor: # base class
|
||||||
if i not in logical_sectors:
|
if i not in logical_sectors:
|
||||||
use_builtin = True
|
use_builtin = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# TODO handle Protected.DOS here
|
# TODO handle Protected.DOS here
|
||||||
|
|
||||||
if not use_builtin:
|
if not use_builtin:
|
||||||
# check for "STY $48;STA $49" at RWTS entry point ($BD00)
|
# check for "STY $48;STA $49" at RWTS entry point ($BD00)
|
||||||
use_builtin = not find.at(0x00, logical_sectors[7], b'\x84\x48\x85\x49')
|
use_builtin = not find.at(0x00, logical_sectors[7], b'\x84\x48\x85\x49')
|
||||||
|
@ -605,13 +707,19 @@ class BasePassportProcessor: # base class
|
||||||
# TODO handle Infocom here
|
# TODO handle Infocom here
|
||||||
|
|
||||||
if use_builtin:
|
if use_builtin:
|
||||||
self.logger.PrintByID("builtin")
|
return self.StartWithUniv()
|
||||||
return rwts
|
|
||||||
|
|
||||||
self.logger.PrintByID("diskrwts")
|
self.logger.PrintByID("diskrwts")
|
||||||
self.g.is_rwts = True
|
self.g.is_rwts = True
|
||||||
return DOS33RWTS(logical_sectors, self.logger)
|
return DOS33RWTS(logical_sectors, self.logger)
|
||||||
|
|
||||||
|
def StartWithUniv(self):
|
||||||
|
"""return Universal RWTS object, log that we're using it, and set global flags appropriately"""
|
||||||
|
self.logger.PrintByID("builtin")
|
||||||
|
self.g.tried_univ = True
|
||||||
|
self.g.is_protdos = False
|
||||||
|
return UniversalRWTS(self.logger)
|
||||||
|
|
||||||
def preprocess(self):
|
def preprocess(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -625,8 +733,8 @@ class BasePassportProcessor: # base class
|
||||||
self.tracks[float(track_num)] = self.g.disk_image.seek(float(track_num))
|
self.tracks[float(track_num)] = self.g.disk_image.seek(float(track_num))
|
||||||
|
|
||||||
# analyze track $00 to create an RWTS
|
# analyze track $00 to create an RWTS
|
||||||
rwts = self.IDBootloader(self.tracks[0])
|
self.rwts = self.IDBootloader(self.tracks[0])
|
||||||
if not rwts: return False
|
if not self.rwts: return False
|
||||||
|
|
||||||
# initialize all patchers
|
# initialize all patchers
|
||||||
for P in self.patcher_classes:
|
for P in self.patcher_classes:
|
||||||
|
@ -635,15 +743,15 @@ class BasePassportProcessor: # base class
|
||||||
# main loop - loop through disk from track $22 down to track $00
|
# main loop - loop through disk from track $22 down to track $00
|
||||||
for track_num in range(0x22, -1, -1):
|
for track_num in range(0x22, -1, -1):
|
||||||
if track_num == 0 and self.g.tried_univ:
|
if track_num == 0 and self.g.tried_univ:
|
||||||
rwts = UniversalRWTSIgnoreEpilogues(self.logger)
|
self.rwts = UniversalRWTSIgnoreEpilogues(self.logger)
|
||||||
should_run_patchers = False
|
should_run_patchers = False
|
||||||
self.g.track = track_num
|
self.g.track = track_num
|
||||||
physical_sectors = rwts.decode_track(self.tracks[track_num], self.burn)
|
physical_sectors = self.rwts.decode_track(self.tracks[track_num], self.burn)
|
||||||
if 0x0F not in physical_sectors:
|
if 0x0F not in physical_sectors:
|
||||||
if self.SkipTrack(rwts, track_num, self.tracks[track_num]):
|
if self.SkipTrack(track_num, self.tracks[track_num]):
|
||||||
self.save_track(rwts, track_num, None)
|
self.save_track(track_num, None)
|
||||||
continue
|
continue
|
||||||
if len(physical_sectors) < rwts.sectors_per_track:
|
if len(physical_sectors) < self.rwts.sectors_per_track:
|
||||||
# TODO wrong in case where we switch mid-track.
|
# TODO wrong in case where we switch mid-track.
|
||||||
# Need to save the sectors that worked with the original RWTS
|
# Need to save the sectors that worked with the original RWTS
|
||||||
# then append the ones that worked with the universal RWTS
|
# then append the ones that worked with the universal RWTS
|
||||||
|
@ -651,25 +759,25 @@ class BasePassportProcessor: # base class
|
||||||
self.logger.PrintByID("fail")
|
self.logger.PrintByID("fail")
|
||||||
return False
|
return False
|
||||||
self.logger.PrintByID("switch", {"sector":0x0F}) # TODO find exact sector
|
self.logger.PrintByID("switch", {"sector":0x0F}) # TODO find exact sector
|
||||||
rwts = UniversalRWTS(self.logger)
|
self.rwts = UniversalRWTS(self.logger)
|
||||||
self.g.tried_univ = True
|
self.g.tried_univ = True
|
||||||
physical_sectors = rwts.decode_track(self.tracks[track_num], self.burn)
|
physical_sectors = self.rwts.decode_track(self.tracks[track_num], self.burn)
|
||||||
if len(physical_sectors) < rwts.sectors_per_track:
|
if len(physical_sectors) < self.rwts.sectors_per_track:
|
||||||
self.logger.PrintByID("fail") # TODO find exact sector
|
self.logger.PrintByID("fail") # TODO find exact sector
|
||||||
return False
|
return False
|
||||||
self.save_track(rwts, track_num, physical_sectors)
|
self.save_track(track_num, physical_sectors)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def save_track(self, rwts, track_num, physical_sectors):
|
def save_track(self, track_num, physical_sectors):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def apply_patches(self, logical_sectors, patches):
|
def apply_patches(self, logical_sectors, patches):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Verify(BasePassportProcessor):
|
class Verify(BasePassportProcessor):
|
||||||
def save_track(self, rwts, track_num, physical_sectors):
|
def save_track(self, track_num, physical_sectors):
|
||||||
if not physical_sectors: return {}
|
if not physical_sectors: return {}
|
||||||
logical_sectors = rwts.reorder_to_logical_sectors(physical_sectors)
|
logical_sectors = self.rwts.reorder_to_logical_sectors(physical_sectors)
|
||||||
should_run_patchers = (len(physical_sectors) == 16) # TODO
|
should_run_patchers = (len(physical_sectors) == 16) # TODO
|
||||||
if should_run_patchers:
|
if should_run_patchers:
|
||||||
for patcher in self.patchers:
|
for patcher in self.patchers:
|
||||||
|
@ -689,8 +797,8 @@ class Verify(BasePassportProcessor):
|
||||||
self.logger.PrintByID("passver")
|
self.logger.PrintByID("passver")
|
||||||
|
|
||||||
class Crack(Verify):
|
class Crack(Verify):
|
||||||
def save_track(self, rwts, track_num, physical_sectors):
|
def save_track(self, track_num, physical_sectors):
|
||||||
self.output_tracks[float(track_num)] = Verify.save_track(self, rwts, track_num, physical_sectors)
|
self.output_tracks[float(track_num)] = Verify.save_track(self, track_num, physical_sectors)
|
||||||
|
|
||||||
def apply_patches(self, logical_sectors, patches):
|
def apply_patches(self, logical_sectors, patches):
|
||||||
for patch in patches:
|
for patch in patches:
|
||||||
|
@ -725,7 +833,7 @@ class EDDToWoz(BasePassportProcessor):
|
||||||
self.burn = 2
|
self.burn = 2
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def save_track(self, rwts, track_num, physical_sectors):
|
def save_track(self, track_num, physical_sectors):
|
||||||
track_num = float(track_num)
|
track_num = float(track_num)
|
||||||
track = self.tracks[track_num]
|
track = self.tracks[track_num]
|
||||||
if physical_sectors:
|
if physical_sectors:
|
||||||
|
@ -733,6 +841,7 @@ class EDDToWoz(BasePassportProcessor):
|
||||||
for s in physical_sectors.values():
|
for s in physical_sectors.values():
|
||||||
b.extend(track.bits[s.start_bit_index:s.end_bit_index])
|
b.extend(track.bits[s.start_bit_index:s.end_bit_index])
|
||||||
else:
|
else:
|
||||||
|
# TODO this only works about half the time
|
||||||
b = track.bits[:51021]
|
b = track.bits[:51021]
|
||||||
self.output_tracks[track_num] = wozimage.Track(b, len(b))
|
self.output_tracks[track_num] = wozimage.Track(b, len(b))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"a5count",
|
||||||
|
"a6bc95",
|
||||||
|
"advint",
|
||||||
|
"bademu",
|
||||||
"d5d5f7",
|
"d5d5f7",
|
||||||
"microfun",
|
"microfun",
|
||||||
"rwts",
|
"rwts",
|
||||||
|
|
22
passport/patchers/a5count.py
Normal file
22
passport/patchers/a5count.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from passport.patchers import Patch, Patcher
|
||||||
|
from passport.util import *
|
||||||
|
|
||||||
|
class A5CountPatcher(Patcher):
|
||||||
|
"""nibble count between $A5 and address prologue
|
||||||
|
|
||||||
|
tested on
|
||||||
|
- Game Frame One
|
||||||
|
- Game Frame Two
|
||||||
|
"""
|
||||||
|
def should_run(self, track_num):
|
||||||
|
return self.g.is_pascal
|
||||||
|
|
||||||
|
def run(self, logical_sectors, track_num):
|
||||||
|
offset = find.wild(concat_track(logical_sectors),
|
||||||
|
b'\x07'
|
||||||
|
b'\xE6\x02'
|
||||||
|
b'\xD0\x03'
|
||||||
|
b'\x4C\xA5\x00'
|
||||||
|
b'\xC9\xA5')
|
||||||
|
if offset == -1: return []
|
||||||
|
return [Patch(track_num, offset // 256, 8 + (offset % 256), b'\xD0\x7B', "a5count")]
|
36
passport/patchers/a6bc95.py
Normal file
36
passport/patchers/a6bc95.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from passport.patchers import Patch, Patcher
|
||||||
|
from passport.util import *
|
||||||
|
|
||||||
|
class A6BC95Patcher(Patcher):
|
||||||
|
"""nibble count after $A6 $BC $95 prologue
|
||||||
|
|
||||||
|
tested on
|
||||||
|
- The Secrets of Science Island
|
||||||
|
"""
|
||||||
|
def should_run(self, track_num):
|
||||||
|
return self.g.is_pascal
|
||||||
|
|
||||||
|
def run(self, logical_sectors, track_num):
|
||||||
|
buffy = concat_track(logical_sectors)
|
||||||
|
if -1 == find.wild(buffy,
|
||||||
|
b'\xBD\x8C\xC0'
|
||||||
|
b'\x10\xFB'
|
||||||
|
b'\xC9\xA6'
|
||||||
|
b'\xD0\xED'):
|
||||||
|
return False
|
||||||
|
if -1 == find.wild(buffy,
|
||||||
|
b'\xBD\x8C\xC0'
|
||||||
|
b'\x10\xFB'
|
||||||
|
b'\xC9\xBC'):
|
||||||
|
return False
|
||||||
|
if -1 == find.wild(buffy,
|
||||||
|
b'\xBD\x8C\xC0'
|
||||||
|
b'\x10\xFB'
|
||||||
|
b'\xC9\x95'):
|
||||||
|
return False
|
||||||
|
offset = find.wild(buffy,
|
||||||
|
b'\xAE\xF8\x01'
|
||||||
|
b'\xA9\x0A'
|
||||||
|
b'\x8D\xFE\x01')
|
||||||
|
if offset == -1: return []
|
||||||
|
return [Patch(track_num, offset // 256, offset % 256, b'\x60', "a6bc95")]
|
24
passport/patchers/advint.py
Normal file
24
passport/patchers/advint.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from passport.patchers import Patch, Patcher
|
||||||
|
from passport.util import *
|
||||||
|
|
||||||
|
class AdventureInternationalPatcher(Patcher):
|
||||||
|
"""encrypted protection check on Adventure International disks
|
||||||
|
|
||||||
|
tested on
|
||||||
|
- SAGA1 - Adventureland v2.1-416
|
||||||
|
- SAGA2 - Pirate Adventure v2.1-408
|
||||||
|
- SAGA5 - The Count v2.1-115
|
||||||
|
- SAGA6 - Strange Odyssey v2.1-119
|
||||||
|
"""
|
||||||
|
def should_run(self, track_num):
|
||||||
|
return True # TODO self.g.is_adventure_international
|
||||||
|
|
||||||
|
def run(self, logical_sectors, track_num):
|
||||||
|
buffy = concat_track(logical_sectors)
|
||||||
|
offset = find.wild(buffy,
|
||||||
|
b'\x85' + find.WILDCARD + find.WILDCARD + \
|
||||||
|
b'\x74\x45\x09'
|
||||||
|
b'\xD9\x32'
|
||||||
|
b'\x0C\x30')
|
||||||
|
if offset == -1: return []
|
||||||
|
return [Patch(track_num, offset // 256, offset % 256, b'\xD1\x59\xA7', "advint")]
|
25
passport/patchers/bademu.py
Normal file
25
passport/patchers/bademu.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from passport.patchers import Patch, Patcher
|
||||||
|
from passport.util import *
|
||||||
|
|
||||||
|
class BadEmuPatcher(Patcher):
|
||||||
|
"""RWTS checks for timing bit by checking if data latch is still $D5 after waiting "too long" but this confuses legacy emulators (AppleWin, older versions of MAME) so we patch it for compatibility
|
||||||
|
|
||||||
|
tested on
|
||||||
|
- Dino Dig
|
||||||
|
- Make A Face
|
||||||
|
"""
|
||||||
|
def should_run(self, track_num):
|
||||||
|
return self.g.is_rwts and (track_num == 0)
|
||||||
|
|
||||||
|
def run(self, logical_sectors, track_num):
|
||||||
|
if not find.at(0x4F, logical_sectors[3],
|
||||||
|
b'\xBD\x8C\xC0'
|
||||||
|
b'\x10\xFB'
|
||||||
|
b'\xC9\xD5'
|
||||||
|
b'\xD0\xF0'
|
||||||
|
b'\xEA'
|
||||||
|
b'\xBD\x8C\xC0'
|
||||||
|
b'\xC9\xD5'
|
||||||
|
b'\xF0\x12'): return []
|
||||||
|
return [Patch(0, 3, 0x58, b'\xF0\x06')]
|
||||||
|
return patches
|
|
@ -2,6 +2,19 @@ from passport.patchers import Patch, Patcher
|
||||||
from passport.util import *
|
from passport.util import *
|
||||||
|
|
||||||
class D5D5F7Patcher(Patcher):
|
class D5D5F7Patcher(Patcher):
|
||||||
|
"""nibble count with weird bitstream involving $D5 and $F7 as delimiters
|
||||||
|
|
||||||
|
tested on
|
||||||
|
- Ace Detective
|
||||||
|
- Cat 'n Mouse
|
||||||
|
- Cotton Tales
|
||||||
|
- Dyno-Quest
|
||||||
|
- Easy Street
|
||||||
|
- Fraction-oids
|
||||||
|
- Math Magic
|
||||||
|
- RoboMath
|
||||||
|
- NoteCard Maker
|
||||||
|
"""
|
||||||
def should_run(self, track_num):
|
def should_run(self, track_num):
|
||||||
# TODO
|
# TODO
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -2,6 +2,15 @@ from passport.patchers import Patch, Patcher
|
||||||
from passport.util import *
|
from passport.util import *
|
||||||
|
|
||||||
class MicrofunPatcher(Patcher):
|
class MicrofunPatcher(Patcher):
|
||||||
|
"""RWTS jumps to nibble check after reading certain sectors
|
||||||
|
|
||||||
|
tested on
|
||||||
|
- Station 5
|
||||||
|
- The Heist
|
||||||
|
- Miner 2049er (re-release)
|
||||||
|
- Miner 2049er II
|
||||||
|
- Short Circuit
|
||||||
|
"""
|
||||||
def should_run(self, track_num):
|
def should_run(self, track_num):
|
||||||
return self.g.is_rwts and (track_num == 0)
|
return self.g.is_rwts and (track_num == 0)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from passport.patchers import Patch, Patcher
|
||||||
from passport.util import *
|
from passport.util import *
|
||||||
|
|
||||||
class RWTSPatcher(Patcher):
|
class RWTSPatcher(Patcher):
|
||||||
|
"""RWTS fixups for DOS 3.3-shaped RWTSen"""
|
||||||
def should_run(self, track_num):
|
def should_run(self, track_num):
|
||||||
return self.g.is_rwts and (track_num == 0)
|
return self.g.is_rwts and (track_num == 0)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ from passport.patchers import Patch, Patcher
|
||||||
from passport.util import *
|
from passport.util import *
|
||||||
|
|
||||||
class UniversalE7Patcher(Patcher):
|
class UniversalE7Patcher(Patcher):
|
||||||
|
"""replace remnants of E7 bitstream with a compatible BYTEstream that fools most E7 protection checks
|
||||||
|
|
||||||
|
(invented by qkumba, see PoC||GTFO 0x11 and 4am crack no. 655 Rocky's Boots 4.0 for explanation)
|
||||||
|
"""
|
||||||
e7sector = b'\x00'*0xA0 + b'\xAC\x00'*0x30
|
e7sector = b'\x00'*0xA0 + b'\xAC\x00'*0x30
|
||||||
|
|
||||||
def should_run(self, track_num):
|
def should_run(self, track_num):
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
|
import re
|
||||||
|
|
||||||
WILDCARD = b'\x97'
|
WILDCARD = b'\x97'
|
||||||
|
|
||||||
def wild(source_bytes, search_bytes):
|
def wild(source_bytes, search_bytes):
|
||||||
"""Search source_bytes (bytes object) for the first instance of search_bytes (bytes_object). search_bytes may contain a wildcard that matches any byte, like '.' in a regular expression. Returns index of first match or -1, like string find() method."""
|
"""Search source_bytes (bytes object) for the first instance of search_bytes (bytes_object). search_bytes may contain WILDCARD, which matches any single byte (like "." in a regular expression). Returns index of first match, or -1 if no matches."""
|
||||||
ranges = search_bytes.split(WILDCARD)
|
search_bytes = re.escape(search_bytes).replace(b'\\'+WILDCARD, b'.')
|
||||||
first_index = last_index = source_bytes.find(ranges[0])
|
match = re.search(search_bytes, source_bytes)
|
||||||
if first_index == -1: return -1
|
if match:
|
||||||
last_index += len(ranges[0])
|
return match.start()
|
||||||
for search_range in ranges[1:]:
|
return -1
|
||||||
last_index += 1
|
|
||||||
if not search_range: continue
|
|
||||||
if source_bytes[last_index:last_index + len(search_range)] != search_range: return -1
|
|
||||||
last_index += len(search_range)
|
|
||||||
return first_index
|
|
||||||
|
|
||||||
def wild_at(offset, source_bytes, search_bytes):
|
def wild_at(offset, source_bytes, search_bytes):
|
||||||
"""returns True if the search_bytes was found in source_bytes at offset (search_bytes may include wildcards), otherwise False"""
|
"""returns True if the search_bytes was found in source_bytes at offset (search_bytes may include wildcards), otherwise False"""
|
||||||
return wild(source_bytes[offset:], search_bytes) == 0
|
offset = wild(source_bytes[offset:], search_bytes)
|
||||||
|
return offset == 0
|
||||||
|
|
||||||
def at(offset, source_bytes, search_bytes):
|
def at(offset, source_bytes, search_bytes):
|
||||||
"""returns True if the exact bytes search_bytes was found in source_bytes at offset (no wildcards), otherwise False"""
|
"""returns True if the exact bytes search_bytes was found in source_bytes at offset (no wildcards), otherwise False"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user