mirror of
https://github.com/a2-4am/passport.py.git
synced 2025-01-14 08:32:49 +00:00
more patchers
This commit is contained in:
parent
e924950634
commit
d8f7960335
@ -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)
|
||||
|
@ -3,6 +3,10 @@ __all__ = [
|
||||
"a6bc95",
|
||||
"advint",
|
||||
"bademu",
|
||||
"bademu2",
|
||||
"bbf9",
|
||||
"bootcounter",
|
||||
"border",
|
||||
"d5d5f7",
|
||||
"microfun",
|
||||
"rwts",
|
||||
|
25
passport/patchers/bademu2.py
Normal file
25
passport/patchers/bademu2.py
Normal file
@ -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
|
32
passport/patchers/bbf9.py
Normal file
32
passport/patchers/bbf9.py
Normal file
@ -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")]
|
25
passport/patchers/bootcounter.py
Normal file
25
passport/patchers/bootcounter.py
Normal file
@ -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
|
22
passport/patchers/border.py
Normal file
22
passport/patchers/border.py
Normal file
@ -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")]
|
Loading…
x
Reference in New Issue
Block a user