mirror of
https://github.com/robmcmullen/atrcopy.git
synced 2024-10-31 23:09:28 +00:00
Added rom image conversion to atari cart images
This commit is contained in:
parent
a2831c0edf
commit
50488fc2e5
@ -19,7 +19,7 @@ from .dos33 import Dos33DiskImage
|
|||||||
from .kboot import KBootImage, add_xexboot_header
|
from .kboot import KBootImage, add_xexboot_header
|
||||||
from .segments import SegmentData, SegmentSaver, DefaultSegment, EmptySegment, ObjSegment, RawSectorsSegment, SegmentedFileSegment, user_bit_mask, match_bit_mask, comment_bit_mask, data_style, selected_bit_mask, diff_bit_mask, not_user_bit_mask, interleave_segments, SegmentList, get_style_mask, get_style_bits
|
from .segments import SegmentData, SegmentSaver, DefaultSegment, EmptySegment, ObjSegment, RawSectorsSegment, SegmentedFileSegment, user_bit_mask, match_bit_mask, comment_bit_mask, data_style, selected_bit_mask, diff_bit_mask, not_user_bit_mask, interleave_segments, SegmentList, get_style_mask, get_style_bits
|
||||||
from .spartados import SpartaDosDiskImage
|
from .spartados import SpartaDosDiskImage
|
||||||
from .cartridge import A8CartHeader, AtariCartImage
|
from .cartridge import A8CartHeader, AtariCartImage, RomImage
|
||||||
from .parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, guess_container, iter_parsers, iter_known_segment_parsers, mime_parse_order, parsers_for_filename
|
from .parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, guess_container, iter_parsers, iter_known_segment_parsers, mime_parse_order, parsers_for_filename
|
||||||
from .magic import guess_detail_for_mime
|
from .magic import guess_detail_for_mime
|
||||||
from .utils import to_numpy, text_to_int
|
from .utils import to_numpy, text_to_int
|
||||||
|
@ -119,6 +119,7 @@ class A8CartHeader:
|
|||||||
('checksum', '>u4'),
|
('checksum', '>u4'),
|
||||||
('unused','>u4')
|
('unused','>u4')
|
||||||
])
|
])
|
||||||
|
nominal_length = format.itemsize
|
||||||
file_format = "Cart"
|
file_format = "Cart"
|
||||||
|
|
||||||
def __init__(self, bytes=None, create=False):
|
def __init__(self, bytes=None, create=False):
|
||||||
@ -138,7 +139,7 @@ class A8CartHeader:
|
|||||||
self.main_origin = 0
|
self.main_origin = 0
|
||||||
self.possible_types = set()
|
self.possible_types = set()
|
||||||
if create:
|
if create:
|
||||||
self.header_offset = 16
|
self.header_offset = self.nominal_length
|
||||||
self.check_size(0)
|
self.check_size(0)
|
||||||
if bytes is None:
|
if bytes is None:
|
||||||
return
|
return
|
||||||
@ -149,7 +150,7 @@ class A8CartHeader:
|
|||||||
raise errors.InvalidCartHeader
|
raise errors.InvalidCartHeader
|
||||||
self.cart_type = int(values[1])
|
self.cart_type = int(values[1])
|
||||||
self.crc = int(values[2])
|
self.crc = int(values[2])
|
||||||
self.header_offset = 16
|
self.header_offset = self.nominal_length
|
||||||
self.set_type(self.cart_type)
|
self.set_type(self.cart_type)
|
||||||
else:
|
else:
|
||||||
raise errors.InvalidCartHeader
|
raise errors.InvalidCartHeader
|
||||||
@ -160,8 +161,15 @@ class A8CartHeader:
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.header_offset
|
return self.header_offset
|
||||||
|
|
||||||
|
@property
|
||||||
|
def valid(self):
|
||||||
|
return self.cart_type != -1
|
||||||
|
|
||||||
|
def calc_crc_from_data(self, data):
|
||||||
|
self.crc = 0
|
||||||
|
|
||||||
def to_array(self):
|
def to_array(self):
|
||||||
raw = np.zeros([16], dtype=np.uint8)
|
raw = np.zeros([self.nominal_length], dtype=np.uint8)
|
||||||
values = raw.view(dtype=self.format)[0]
|
values = raw.view(dtype=self.format)[0]
|
||||||
values[0] = b'CART'
|
values[0] = b'CART'
|
||||||
values[1] = self.cart_type
|
values[1] = self.cart_type
|
||||||
@ -189,7 +197,7 @@ class A8CartHeader:
|
|||||||
def check_size(self, size):
|
def check_size(self, size):
|
||||||
self.possible_types = set()
|
self.possible_types = set()
|
||||||
k, r = divmod(size, 1024)
|
k, r = divmod(size, 1024)
|
||||||
if r == 0 or r == 16:
|
if r == 0 or r == self.nominal_length:
|
||||||
for i, t in enumerate(known_cart_types):
|
for i, t in enumerate(known_cart_types):
|
||||||
valid_size = t[0]
|
valid_size = t[0]
|
||||||
if k == valid_size:
|
if k == valid_size:
|
||||||
@ -205,7 +213,7 @@ class BaseAtariCartImage(DiskImageBase):
|
|||||||
try:
|
try:
|
||||||
self.header = A8CartHeader(data)
|
self.header = A8CartHeader(data)
|
||||||
except errors.InvalidCartHeader:
|
except errors.InvalidCartHeader:
|
||||||
raise errors.InvalidDiskImage("Missing cart header")
|
self.header = A8CartHeader()
|
||||||
|
|
||||||
def strict_check(self):
|
def strict_check(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -217,10 +225,10 @@ class BaseAtariCartImage(DiskImageBase):
|
|||||||
self.header.set_type(self.cart_type)
|
self.header.set_type(self.cart_type)
|
||||||
|
|
||||||
def check_size(self):
|
def check_size(self):
|
||||||
if self.header is None:
|
if not self.header.valid:
|
||||||
return
|
return
|
||||||
k, rem = divmod((len(self) - len(self.header)), 1024)
|
k, rem = divmod((len(self) - len(self.header)), 1024)
|
||||||
c = get_cart(self.cart_type)
|
c = get_cart(self.header.cart_type)
|
||||||
log.debug("checking type=%d, k=%d, rem=%d for %s, %s" % (self.cart_type, k, rem, c[1], c[2]))
|
log.debug("checking type=%d, k=%d, rem=%d for %s, %s" % (self.cart_type, k, rem, c[1], c[2]))
|
||||||
if rem > 0:
|
if rem > 0:
|
||||||
raise errors.InvalidDiskImage("Cart not multiple of 1K")
|
raise errors.InvalidDiskImage("Cart not multiple of 1K")
|
||||||
@ -252,13 +260,31 @@ class BaseAtariCartImage(DiskImageBase):
|
|||||||
segments.append(s)
|
segments.append(s)
|
||||||
return segments
|
return segments
|
||||||
|
|
||||||
|
def create_emulator_boot_segment(self):
|
||||||
|
print(self.segments)
|
||||||
|
h = self.header
|
||||||
|
k, rem = divmod(len(self), 1024)
|
||||||
|
if rem == 0:
|
||||||
|
h.calc_crc_from_data(self.bytes)
|
||||||
|
data_with_header = np.empty(len(self) + h.nominal_length, dtype=np.uint8)
|
||||||
|
data_with_header[0:h.nominal_length] = h.to_array()
|
||||||
|
data_with_header[h.nominal_length:] = self.bytes
|
||||||
|
r = SegmentData(data_with_header)
|
||||||
|
else:
|
||||||
|
r = self.rawdata
|
||||||
|
s = ObjSegment(r, 0, 0, self.header.main_origin, name="Cart image")
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
class AtariCartImage(BaseAtariCartImage):
|
class AtariCartImage(BaseAtariCartImage):
|
||||||
def __init__(self, rawdata, cart_type, filename=""):
|
def __init__(self, rawdata, cart_type, filename=""):
|
||||||
|
c = get_cart(cart_type)
|
||||||
self.cart_type = cart_type
|
self.cart_type = cart_type
|
||||||
DiskImageBase.__init__(self, rawdata, filename)
|
DiskImageBase.__init__(self, rawdata, filename)
|
||||||
|
|
||||||
def strict_check(self):
|
def strict_check(self):
|
||||||
|
if not self.header.valid:
|
||||||
|
raise errors.InvalidDiskImage("Missing cart header")
|
||||||
if self.header.cart_type != self.cart_type:
|
if self.header.cart_type != self.cart_type:
|
||||||
raise errors.InvalidDiskImage("Cart type doesn't match type defined in header")
|
raise errors.InvalidDiskImage("Cart type doesn't match type defined in header")
|
||||||
|
|
||||||
@ -267,12 +293,14 @@ class Atari8bitCartImage(BaseAtariCartImage):
|
|||||||
def strict_check(self):
|
def strict_check(self):
|
||||||
if "5200" in self.header.cart_name:
|
if "5200" in self.header.cart_name:
|
||||||
raise errors.InvalidDiskImage("5200 Carts don't work in the home computers.")
|
raise errors.InvalidDiskImage("5200 Carts don't work in the home computers.")
|
||||||
|
AtariCartImage.strict_check(self)
|
||||||
|
|
||||||
|
|
||||||
class Atari5200CartImage(BaseAtariCartImage):
|
class Atari5200CartImage(BaseAtariCartImage):
|
||||||
def strict_check(self):
|
def strict_check(self):
|
||||||
if "5200" not in self.header.cart_name:
|
if "5200" not in self.header.cart_name:
|
||||||
raise errors.InvalidDiskImage("Home computer carts don't work in the 5200.")
|
raise errors.InvalidDiskImage("Home computer carts don't work in the 5200.")
|
||||||
|
AtariCartImage.strict_check(self)
|
||||||
|
|
||||||
|
|
||||||
def add_cart_header(bytes):
|
def add_cart_header(bytes):
|
||||||
|
@ -3,8 +3,8 @@ from __future__ import division
|
|||||||
from builtins import object
|
from builtins import object
|
||||||
from mock import *
|
from mock import *
|
||||||
|
|
||||||
from atrcopy import AtariCartImage, SegmentData
|
from atrcopy import AtariCartImage, SegmentData, RomImage, errors
|
||||||
from atrcopy import errors
|
from atrcopy.cartridge import known_cart_types
|
||||||
|
|
||||||
|
|
||||||
class TestAtariCart:
|
class TestAtariCart:
|
||||||
@ -17,26 +17,24 @@ class TestAtariCart:
|
|||||||
data[4:8].view(">u4")[0] = cart_type
|
data[4:8].view(">u4")[0] = cart_type
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def test_unbanked(self):
|
@pytest.mark.parametrize("k_size,cart_type", [
|
||||||
carts = [
|
|
||||||
(8, 1),
|
(8, 1),
|
||||||
(16, 2),
|
(16, 2),
|
||||||
(8, 21),
|
(8, 21),
|
||||||
(2, 57),
|
(2, 57),
|
||||||
(4, 58),
|
(4, 58),
|
||||||
(4, 59),
|
(4, 59),
|
||||||
]
|
])
|
||||||
for k_size, cart_type in carts:
|
def test_unbanked(self, k_size, cart_type):
|
||||||
data = self.get_cart(k_size, cart_type)
|
data = self.get_cart(k_size, cart_type)
|
||||||
rawdata = SegmentData(data)
|
rawdata = SegmentData(data)
|
||||||
image = AtariCartImage(rawdata, cart_type)
|
image = AtariCartImage(rawdata, cart_type)
|
||||||
image.parse_segments()
|
image.parse_segments()
|
||||||
assert len(image.segments) == 2
|
assert len(image.segments) == 2
|
||||||
assert len(image.segments[0]) == 16
|
assert len(image.segments[0]) == 16
|
||||||
assert len(image.segments[1]) == k_size * 1024
|
assert len(image.segments[1]) == k_size * 1024
|
||||||
|
|
||||||
def test_banked(self):
|
@pytest.mark.parametrize("k_size,main_size,banked_size,cart_type", [
|
||||||
carts = [
|
|
||||||
(32, 8, 8, 12),
|
(32, 8, 8, 12),
|
||||||
(64, 8, 8, 13),
|
(64, 8, 8, 13),
|
||||||
(64, 8, 8, 67),
|
(64, 8, 8, 67),
|
||||||
@ -44,16 +42,16 @@ class TestAtariCart:
|
|||||||
(256, 8, 8, 23),
|
(256, 8, 8, 23),
|
||||||
(512, 8, 8, 24),
|
(512, 8, 8, 24),
|
||||||
(1024, 8, 8, 25),
|
(1024, 8, 8, 25),
|
||||||
]
|
])
|
||||||
for k_size, main_size, banked_size, cart_type in carts:
|
def test_banked(self, k_size, main_size, banked_size, cart_type):
|
||||||
data = self.get_cart(k_size, cart_type)
|
data = self.get_cart(k_size, cart_type)
|
||||||
rawdata = SegmentData(data)
|
rawdata = SegmentData(data)
|
||||||
image = AtariCartImage(rawdata, cart_type)
|
image = AtariCartImage(rawdata, cart_type)
|
||||||
image.parse_segments()
|
image.parse_segments()
|
||||||
assert len(image.segments) == 1 + 1 + (k_size - main_size) //banked_size
|
assert len(image.segments) == 1 + 1 + (k_size - main_size) //banked_size
|
||||||
assert len(image.segments[0]) == 16
|
assert len(image.segments[0]) == 16
|
||||||
assert len(image.segments[1]) == main_size * 1024
|
assert len(image.segments[1]) == main_size * 1024
|
||||||
assert len(image.segments[2]) == banked_size * 1024
|
assert len(image.segments[2]) == banked_size * 1024
|
||||||
|
|
||||||
def test_bad(self):
|
def test_bad(self):
|
||||||
k_size = 32
|
k_size = 32
|
||||||
@ -74,9 +72,55 @@ class TestAtariCart:
|
|||||||
image = AtariCartImage(rawdata, 1337)
|
image = AtariCartImage(rawdata, 1337)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRomCart:
|
||||||
|
def setup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_rom(self, k_size):
|
||||||
|
data = np.zeros((k_size * 1024), dtype=np.uint8)
|
||||||
|
return data
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("k_size", [1, 2, 4, 8, 16, 32, 64])
|
||||||
|
def test_typical_rom_sizes(self, k_size):
|
||||||
|
data = self.get_rom(k_size)
|
||||||
|
rawdata = SegmentData(data)
|
||||||
|
rom_image = RomImage(rawdata)
|
||||||
|
rom_image.strict_check()
|
||||||
|
rom_image.parse_segments()
|
||||||
|
assert len(rom_image.segments) == 1
|
||||||
|
assert len(rom_image.segments[0]) == k_size * 1024
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("k_size", [1, 2, 4, 8, 16, 32, 64])
|
||||||
|
def test_invalid_rom_sizes(self, k_size):
|
||||||
|
data = np.zeros((k_size * 1024) + 17, dtype=np.uint8)
|
||||||
|
rawdata = SegmentData(data)
|
||||||
|
with pytest.raises(errors.InvalidDiskImage):
|
||||||
|
rom_image = RomImage(rawdata)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cart", known_cart_types)
|
||||||
|
def test_conversion_to_atari_cart(self, cart):
|
||||||
|
cart_type = cart[0]
|
||||||
|
name = cart[1]
|
||||||
|
k_size = cart[2]
|
||||||
|
if "Bounty" in name:
|
||||||
|
return
|
||||||
|
data = self.get_rom(k_size)
|
||||||
|
rawdata = SegmentData(data)
|
||||||
|
rom_image = RomImage(rawdata)
|
||||||
|
rom_image.strict_check()
|
||||||
|
rom_image.parse_segments()
|
||||||
|
new_cart_image = AtariCartImage(rawdata, cart_type)
|
||||||
|
new_cart_image.relaxed_check()
|
||||||
|
new_cart_image.parse_segments()
|
||||||
|
assert new_cart_image.header.valid
|
||||||
|
s = new_cart_image.create_emulator_boot_segment()
|
||||||
|
assert len(s) == len(rawdata) + new_cart_image.header.nominal_length
|
||||||
|
assert s[0:4].tobytes() == b'CART'
|
||||||
|
assert s[4:8].view(dtype=">u4") == cart_type
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
from atrcopy.parsers import mime_parse_order
|
||||||
print("\n".join(mime_parse_order))
|
print("\n".join(mime_parse_order))
|
||||||
|
|
||||||
t = TestAtariCart()
|
t = TestAtariCart()
|
||||||
|
Loading…
Reference in New Issue
Block a user