From d8f7960335a585646cea445869d4d1d2f3e4631c Mon Sep 17 00:00:00 2001 From: 4am Date: Sun, 27 May 2018 10:24:01 -0400 Subject: [PATCH] more patchers --- passport/__init__.py | 145 ++++++++++++++++++++----------- passport/patchers/__init__.py | 4 + passport/patchers/bademu2.py | 25 ++++++ passport/patchers/bbf9.py | 32 +++++++ passport/patchers/bootcounter.py | 25 ++++++ passport/patchers/border.py | 22 +++++ 6 files changed, 203 insertions(+), 50 deletions(-) create mode 100644 passport/patchers/bademu2.py create mode 100644 passport/patchers/bbf9.py create mode 100644 passport/patchers/bootcounter.py create mode 100644 passport/patchers/border.py diff --git a/passport/__init__.py b/passport/__init__.py index a414b56..3dc4e3b 100755 --- a/passport/__init__.py +++ b/passport/__init__.py @@ -139,7 +139,11 @@ class RWTS: self.sector_order = sector_order self.nibble_translate_table = nibble_translate_table self.logger = logger or SilentLogger + self.track_num = 0 + def seek(self, track_num): + self.track_num = track_num + def reorder_to_logical_sectors(self, sectors): logical = {} for k, v in sectors.items(): @@ -218,14 +222,17 @@ class RWTS: # if we can't even find a single address prologue, just give up self.logger.debug("can't find a single address prologue so LGTM or whatever") break + # for edd->woz conversion, only save some of the bits preceding + # the address prologue if track.bit_index - start_bit_index > 256: start_bit_index = track.bit_index - 256 # decode address field address_field = self.address_field_at_point(track) + self.logger.debug("found sector %s" % hex(address_field.sector_num)[2:].upper()) if address_field.sector_num in verified_sectors: # the sector we just found is a sector we've already decoded # properly, so skip it - self.logger.debug("duplicate sector %d" % address_field.sector_num) + self.logger.debug("duplicate sector %d, continuing" % address_field.sector_num) continue if address_field.sector_num > self.sectors_per_track: # found a weird sector whose ID is out of range @@ -240,21 +247,16 @@ class RWTS: # verifying the address field epilogue failed, but this is # not necessarily fatal because there might be another copy # of this sector later + self.logger.debug("verify_address_epilogue_at_point failed, continuing") continue if not self.find_data_prologue(track): # if we can't find a data field prologue, just give up - self.logger.debug(repr(self.data_prologue)) + self.logger.debug("find_data_prologue failed, giving up") break # read and decode the data field, and verify the data checksum decoded = self.data_field_at_point(track) if not decoded: - self.logger.debug("data_field_at_point failed") -# if DEBUG and address_field.sector_num == 0x0A: -# DEBUG_CACHE.append(track.bits[start_bit_index:track.bit_index]) -# if len(DEBUG_CACHE) == 2: -# import code -# cache = DEBUG_CACHE -# code.interact(local=locals()) + self.logger.debug("data_field_at_point failed, continuing") # decoding data field failed, but this is not necessarily fatal # because there might be another copy of this sector later continue @@ -331,44 +333,54 @@ class UniversalRWTSIgnoreEpilogues(UniversalRWTS): class DOS33RWTS(RWTS): def __init__(self, logical_sectors, logger): - address_prologue = (logical_sectors[3][0x55], - logical_sectors[3][0x5F], - logical_sectors[3][0x6A]) - address_epilogue = (logical_sectors[3][0x91], - logical_sectors[3][0x9B]) - data_prologue = (logical_sectors[2][0xE7], - logical_sectors[2][0xF1], - logical_sectors[2][0xFC]) - data_epilogue = (logical_sectors[3][0x35], - logical_sectors[3][0x3F]) - nibble_translate_table = {} - for nibble in range(0x96, 0x100): - nibble_translate_table[nibble] = logical_sectors[4][nibble] + self.reset(logical_sectors) RWTS.__init__(self, sectors_per_track=16, - address_prologue=address_prologue, - address_epilogue=address_epilogue, - data_prologue=data_prologue, - data_epilogue=data_epilogue, - nibble_translate_table=nibble_translate_table, + address_prologue=self.address_prologue, + address_epilogue=self.address_epilogue, + data_prologue=self.data_prologue, + data_epilogue=self.data_epilogue, + nibble_translate_table=self.nibble_translate_table, logger=logger) -class D5TimingBitRWTS(RWTS): - def __init__(self, logical_sectors, logger): - data_prologue = (logical_sectors[2][0xE7], - 0xAA, - logical_sectors[2][0xFC]) - data_epilogue = (logical_sectors[3][0x35], - 0xAA) - nibble_translate_table = {} + def reset(self, logical_sectors): + self.address_prologue = (logical_sectors[3][0x55], + logical_sectors[3][0x5F], + logical_sectors[3][0x6A]) + self.address_epilogue = (logical_sectors[3][0x91], + logical_sectors[3][0x9B]) + self.data_prologue = (logical_sectors[2][0xE7], + logical_sectors[2][0xF1], + logical_sectors[2][0xFC]) + self.data_epilogue = (logical_sectors[3][0x35], + logical_sectors[3][0x3F]) + self.nibble_translate_table = {} for nibble in range(0x96, 0x100): - nibble_translate_table[nibble] = logical_sectors[4][nibble] - RWTS.__init__(self, - sectors_per_track=16, - data_prologue=data_prologue, - data_epilogue=data_epilogue, - nibble_translate_table=nibble_translate_table, - logger=logger) + self.nibble_translate_table[nibble] = logical_sectors[4][nibble] + +class BorderRWTS(DOS33RWTS): + # TODO doesn't work yet, not sure why + def reset(self, logical_sectors): + DOS33RWTS.reset(self, logical_sectors) + self.address_prologue = (logical_sectors[9][0x16], + logical_sectors[9][0x1B], + logical_sectors[9][0x20]) + self.address_epilogue = (logical_sectors[9][0x25], + logical_sectors[9][0x2A]) + self.data_prologue = (logical_sectors[8][0xFD], + logical_sectors[9][0x02], + logical_sectors[9][0x02]) + self.data_epilogue = (logical_sectors[9][0x0C], + logical_sectors[9][0x11]) + +class D5TimingBitRWTS(DOS33RWTS): + def reset(self, logical_sectors): + DOS33RWTS.reset(self, logical_sectors) + self.data_prologue = (logical_sectors[2][0xE7], + 0xAA, + logical_sectors[2][0xFC]) + self.data_epilogue = (logical_sectors[3][0x35], + 0xAA) def find_address_prologue(self, track): starting_revolutions = track.revolutions @@ -400,7 +412,7 @@ class BasePassportProcessor: # base class #JMPB660Patcher, #JMPB720Patcher, bademu.BadEmuPatcher, - #BadEmu2Patcher, + bademu2.BadEmu2Patcher, rwts.RWTSPatcher, #RWTSLogPatcher, #MECC1Patcher, @@ -412,7 +424,7 @@ class BasePassportProcessor: # base class #DavidBB03Patcher, #RWTSSwapPatcher, #RWTSSwap2Patcher, - #BorderPatcher, + border.BorderPatcher, #JMPAE8EPatcher, #JMPBBFEPatcher, #DatasoftPatcher, @@ -437,7 +449,7 @@ class BasePassportProcessor: # base class #ProDOSRWTSPatcher, #ProDOS6APatcher, #ProDOSMECCPatcher, - #BBF9Patcher, + bbf9.BBF9Patcher, #MemoryConfigPatcher, #OriginPatcher, #RWTSSwapMECCPatcher, @@ -450,7 +462,7 @@ class BasePassportProcessor: # base class #EAPatcher, #GamcoPatcher, #OptimumPatcher, - #BootCounterPatcher, + bootcounter.BootCounterPatcher, #JMPB412Patcher, #JMPB400Patcher, advint.AdventureInternationalPatcher, @@ -680,6 +692,8 @@ class BasePassportProcessor: # base class else: self.logger.PrintByID("dos33boot0") logical_sectors = temporary_rwts_for_t00.reorder_to_logical_sectors(physical_sectors) + if border.BorderPatcher(self.g).run(logical_sectors, 0): + return BorderRWTS(logical_sectors, self.logger) return self.TraceDOS33(logical_sectors) # TODO JSR08B3 # TODO MECC fastloader @@ -733,10 +747,9 @@ class BasePassportProcessor: # base class # LDX $1FE8 e.g. Pinball Construction Set (1983) use_builtin = find.at(0x43, logical_sectors[8], b'\xAE\xE8\x1F') if not use_builtin: - # check for D5+timing+bit RWTS - if find.at(0x58, logical_sectors[3], b'\xEA\xBD\x8C\xC0\xC9\xD5'): + # check for D5+timingbit RWTS + if find.at(0x59, logical_sectors[3], b'\xBD\x8C\xC0\xC9\xD5'): self.logger.PrintByID("diskrwts") - self.g.is_rwts = True return D5TimingBitRWTS(logical_sectors, self.logger) # TODO handle Milliken here @@ -747,7 +760,6 @@ class BasePassportProcessor: # base class return self.StartWithUniv() self.logger.PrintByID("diskrwts") - self.g.is_rwts = True return DOS33RWTS(logical_sectors, self.logger) def StartWithUniv(self): @@ -780,6 +792,7 @@ class BasePassportProcessor: # base class # main loop - loop through disk from track $22 down to track $00 for track_num in range(0x22, -1, -1): self.g.track = track_num + self.rwts.seek(track_num) self.logger.debug("Seeking to track %s" % hex(self.g.track)) try_again = True while try_again: @@ -787,6 +800,8 @@ class BasePassportProcessor: # base class physical_sectors = self.rwts.decode_track(self.tracks[track_num], self.burn) if len(physical_sectors) == self.rwts.sectors_per_track: continue + else: + self.logger.debug("found %d sectors" % len(physical_sectors)) if (0x0F not in physical_sectors) and self.SkipTrack(track_num, self.tracks[track_num]): physical_sectors = None continue @@ -815,11 +830,41 @@ class BasePassportProcessor: # base class pass class Verify(BasePassportProcessor): + def AnalyzeT00(self, logical_sectors): + self.g.is_boot1 = find.at(0x00, logical_sectors[1], + b'\x8E\xE9\xB7\x8E\xF7\xB7\xA9\x01' + b'\x8D\xF8\xB7\x8D\xEA\xB7\xAD\xE0' + b'\xB7\x8D\xE1\xB7\xA9\x02\x8D\xEC' + b'\xB7\xA9\x04\x8D\xED\xB7\xAC\xE7' + b'\xB7\x88\x8C\xF1\xB7\xA9\x01\x8D' + b'\xF4\xB7\x8A\x4A\x4A\x4A\x4A\xAA' + b'\xA9\x00\x9D\xF8\x04\x9D\x78\x04') + self.g.is_master = find.at(0x00, logical_sectors[1], + b'\x8E\xE9\x37\x8E\xF7\x37\xA9\x01' + b'\x8D\xF8\x37\x8D\xEA\x37\xAD\xE0' + b'\x37\x8D\xE1\x37\xA9\x02\x8D\xEC' + b'\x37\xA9\x04\x8D\xED\x37\xAC\xE7' + b'\x37\x88\x8C\xF1\x37\xA9\x01\x8D' + b'\xF4\x37\x8A\x4A\x4A\x4A\x4A\xAA' + b'\xA9\x00\x9D\xF8\x04\x9D\x78\x04') + self.g.is_rwts = find.wild_at(0x00, logical_sectors[7], + b'\x84\x48\x85\x49\xA0\x02\x8C' + find.WILDCARD + \ + find.WILDCARD + b'\xA0\x04\x8C' + find.WILDCARD + find.WILDCARD + b'\xA0\x01' + \ + b'\xB1\x48\xAA\xA0\x0F\xD1\x48\xF0' + b'\x1B\x8A\x48\xB1\x48\xAA\x68\x48' + b'\x91\x48\xBD\x8E\xC0\xA0\x08\xBD' + b'\x8C\xC0\xDD\x8C\xC0\xD0\xF6\x88' + b'\xD0\xF8\x68\xAA\xBD\x8E\xC0\xBD' + b'\x8C\xC0\xA0\x08\xBD\x8C\xC0\x48') + def save_track(self, track_num, physical_sectors): if not physical_sectors: return {} logical_sectors = self.rwts.reorder_to_logical_sectors(physical_sectors) should_run_patchers = (len(physical_sectors) == 16) # TODO if should_run_patchers: + if track_num == 0: + # set additional globals for patchers to use + self.AnalyzeT00(logical_sectors) for patcher in self.patchers: if patcher.should_run(track_num): patches = patcher.run(logical_sectors, track_num) diff --git a/passport/patchers/__init__.py b/passport/patchers/__init__.py index 16bbd54..f4bf34e 100644 --- a/passport/patchers/__init__.py +++ b/passport/patchers/__init__.py @@ -3,6 +3,10 @@ __all__ = [ "a6bc95", "advint", "bademu", + "bademu2", + "bbf9", + "bootcounter", + "border", "d5d5f7", "microfun", "rwts", diff --git a/passport/patchers/bademu2.py b/passport/patchers/bademu2.py new file mode 100644 index 0000000..66573d8 --- /dev/null +++ b/passport/patchers/bademu2.py @@ -0,0 +1,25 @@ +from passport.patchers import Patch, Patcher +from passport.util import * + +class BadEmu2Patcher(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'\x4A' + b'\xC9\x6A' + b'\xD0\xEF' + b'\xBD\x8C\xC0' + b'\xC9\xD5' + b'\xF0\x12'): return [] + return [Patch(0, 3, 0x59, b'\xF0\x05')] + return patches diff --git a/passport/patchers/bbf9.py b/passport/patchers/bbf9.py new file mode 100644 index 0000000..2e58e11 --- /dev/null +++ b/passport/patchers/bbf9.py @@ -0,0 +1,32 @@ +from passport.patchers import Patch, Patcher +from passport.util import * + +class BBF9Patcher(Patcher): + """patch nibble check seen in Sunburst disks 1988 and later + +see write-up of 4am crack no. 1165 Muppet Slate + +tested on +- Muppet Slate (1988) +- Memory Building Blocks (1989) +- Odd One Out (1989) +- Regrouping (1989) +- Simon Says (1989) +- Teddy and Iggy (1990) +- 1-2-3 Sequence Me (1991) +""" + def should_run(self, track_num): + return self.g.is_prodos + + def run(self, logical_sectors, track_num): + buffy = concat_track(logical_sectors) + if -1 == find.wild(buffy, + b'\x8E\xC0' + b'\x18' + b'\xA5' + find.WILDCARD + \ + b'\x69\x8C' + b'\x8D'): return [] + offset = find.wild(buffy, + b'\xBD\x89\xC0') + if offset == -1: return [] + return [Patch(track_num, offset // 256, offset % 256, b'\x18\x60', "bbf9")] diff --git a/passport/patchers/bootcounter.py b/passport/patchers/bootcounter.py new file mode 100644 index 0000000..78efa5f --- /dev/null +++ b/passport/patchers/bootcounter.py @@ -0,0 +1,25 @@ +from passport.patchers import Patch, Patcher +from passport.util import * + +class BootCounterPatcher(Patcher): + """MECC "limited backup" disks contain code to self-destruct after a certain number of boots""" + def should_run(self, track_num): + return track_num == 1 + + def run(self, logical_sectors, track_num): + if not find.wild_at(0x00, logical_sectors[0], + b'\xAD\xF3\x03' + b'\x8D\xF4\x03' + b'\x20\x2F\xFB' + b'\x20\x93\xFE' + b'\x20\x89\xFE' + b'\x20\x58\xFC' + b'\xA9\x0A' + b'\x85\x25' + b'\x2C' + find.WILDCARD + find.WILDCARD + \ + b'\xCE\x17\x18' + b'\xD0\x05' + b'\xA9\x80' + b'\x8D\x18\x18'): return [] + return [Patch(1, 0, 0x00, b'\x4C\x03\x1B', "bootcounter")] + return patches diff --git a/passport/patchers/border.py b/passport/patchers/border.py new file mode 100644 index 0000000..8aa3fce --- /dev/null +++ b/passport/patchers/border.py @@ -0,0 +1,22 @@ +from passport.patchers import Patch, Patcher +from passport.util import * + +class BorderPatcher(Patcher): + """RWTS changes prologue and epilogue sequences with an RWTS swapper at $BE5A + +tested on +- Arena +- Early Bird +""" + def should_run(self, track_num): + return self.g.is_boot0 and self.g.is_boot1 and track_num == 0 + + def run(self, logical_sectors, track_num): + if not find.at(0x5A, logical_sectors[8], + b'\xC9\x23' + b'\xB0\xEB' + b'\x0A' + b'\x20\x6C\xBF' + b'\xEA' + b'\xEA'): return [] + return [Patch(0, 8, 0x5A, b'\x48\xA0\x01\xB1\x3C\x6A\x68\x90\x08\x0A', "border")]