diff --git a/Analyser/Static/AppleII/StaticAnalyser.cpp b/Analyser/Static/AppleII/StaticAnalyser.cpp index 166499a72..3f379cf47 100644 --- a/Analyser/Static/AppleII/StaticAnalyser.cpp +++ b/Analyser/Static/AppleII/StaticAnalyser.cpp @@ -13,8 +13,17 @@ Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media & auto target = std::make_unique(); target->media = media; - if(!target->media.disks.empty()) + // If any disks are present, attach a Disk II. + if(!target->media.disks.empty()) { target->disk_controller = Target::DiskController::SixteenSector; + } + + // The emulated SCSI card requires a IIe, so upgrade to that if + // any mass storage is present. + if(!target->media.mass_storage_devices.empty()) { + target->model = Target::Model::EnhancedIIe; + target->scsi_controller = Target::SCSIController::AppleSCSI; + } TargetList targets; targets.push_back(std::move(target)); diff --git a/Analyser/Static/AppleII/Target.hpp b/Analyser/Static/AppleII/Target.hpp index 733d7b79a..030b81947 100644 --- a/Analyser/Static/AppleII/Target.hpp +++ b/Analyser/Static/AppleII/Target.hpp @@ -29,16 +29,24 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl inline constexpr bool always_false_v = false; + using namespace Analyser::Static; namespace { @@ -123,7 +125,21 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: if(extension == "2mg") { // 2MG uses a factory method; defer to it. try { - InsertInstance(result.disks, Storage::Disk::Disk2MG::open(file_name), TargetPlatform::DiskII) + const auto media = Storage::Disk::Disk2MG::open(file_name); + std::visit([&result, &potential_platforms](auto &&arg) { + using Type = typename std::decay::type; + + if constexpr (std::is_same::value) { + // It's valid for no media to be returned. + } else if constexpr (std::is_same::value) { + InsertInstance(result.disks, arg, TargetPlatform::DiskII); + } else if constexpr (std::is_same::value) { + // TODO: or is it Apple IIgs? + InsertInstance(result.mass_storage_devices, arg, TargetPlatform::AppleII); + } else { + static_assert(always_false_v, "Unexpected type encountered."); + } + }, media); } catch(...) {} } diff --git a/Components/5380/ncr5380.cpp b/Components/5380/ncr5380.cpp index 52f3d4570..1d89d4eac 100644 --- a/Components/5380/ncr5380.cpp +++ b/Components/5380/ncr5380.cpp @@ -8,8 +8,18 @@ #include "ncr5380.hpp" +#ifndef NDEBUG +#define NDEBUG +#endif +#define LOG_PREFIX "[5380] " + #include "../../Outputs/Log.hpp" +// TODO: +// +// end_of_dma_ should be set if: /EOP && /DACK && (/RD || /WR); for at least 100ns. + + using namespace NCR::NCR5380; using SCSI::Line; @@ -28,20 +38,16 @@ NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) : void NCR5380::write(int address, uint8_t value, bool) { switch(address & 7) { case 0: -// LOG("[SCSI 0] Set current SCSI bus state to " << PADHEX(2) << int(value)); - data_bus_ = value; + LOG("[0] Set current SCSI bus state to " << PADHEX(2) << int(value)); + data_bus_ = value; if(dma_request_ && dma_operation_ == DMAOperation::Send) { -// printf("w %02x\n", value); - dma_acknowledge_ = true; - dma_request_ = false; - update_control_output(); - bus_.set_device_output(device_id_, bus_output_); + dma_acknowledge(value); } break; case 1: { -// LOG("[SCSI 1] Initiator command register set: " << PADHEX(2) << int(value)); + LOG("[1] Initiator command register set: " << PADHEX(2) << int(value)); initiator_command_ = value; bus_output_ &= ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention); @@ -57,7 +63,7 @@ void NCR5380::write(int address, uint8_t value, bool) { } break; case 2: -// LOG("[SCSI 2] Set mode: " << PADHEX(2) << int(value)); + LOG("[2] Set mode: " << PADHEX(2) << int(value)); mode_ = value; // bit 7: 1 = use block mode DMA mode (if DMA mode is also enabled) @@ -69,6 +75,7 @@ void NCR5380::write(int address, uint8_t value, bool) { // bit 1: 1 = use DMA mode // bit 0: 1 = begin arbitration mode (device ID should be in register 0) arbitration_in_progress_ = false; + phase_mismatch_ = false; switch(mode_ & 0x3) { case 0x0: bus_output_ &= ~SCSI::Line::Busy; @@ -88,31 +95,36 @@ void NCR5380::write(int address, uint8_t value, bool) { bus_.update_observers(); break; } + + // "[The End of DMA Transfer] bit is reset when the DMA MODE bit + // is reset (0) in the Mode Register". + end_of_dma_ &= bool(value & 0x2); + update_control_output(); break; case 3: { -// LOG("[SCSI 3] Set target command: " << PADHEX(2) << int(value)); + LOG("[3] Set target command: " << PADHEX(2) << int(value)); target_command_ = value; update_control_output(); } break; case 4: -// LOG("[SCSI 4] Set select enabled: " << PADHEX(2) << int(value)); + LOG("[4] Set select enabled: " << PADHEX(2) << int(value)); break; case 5: -// LOG("[SCSI 5] Start DMA send: " << PADHEX(2) << int(value)); + LOG("[5] Start DMA send: " << PADHEX(2) << int(value)); dma_operation_ = DMAOperation::Send; break; case 6: -// LOG("[SCSI 6] Start DMA target receive: " << PADHEX(2) << int(value)); + LOG("[6] Start DMA target receive: " << PADHEX(2) << int(value)); dma_operation_ = DMAOperation::TargetReceive; break; case 7: -// LOG("[SCSI 7] Start DMA initiator receive: " << PADHEX(2) << int(value)); + LOG("[7] Start DMA initiator receive: " << PADHEX(2) << int(value)); dma_operation_ = DMAOperation::InitiatorReceive; break; } @@ -136,18 +148,15 @@ void NCR5380::write(int address, uint8_t value, bool) { uint8_t NCR5380::read(int address, bool) { switch(address & 7) { case 0: -// LOG("[SCSI 0] Get current SCSI bus state: " << PADHEX(2) << (bus_.get_state() & 0xff)); + LOG("[0] Get current SCSI bus state: " << PADHEX(2) << (bus_.get_state() & 0xff)); if(dma_request_ && dma_operation_ == DMAOperation::InitiatorReceive) { - dma_acknowledge_ = true; - dma_request_ = false; - update_control_output(); - bus_.set_device_output(device_id_, bus_output_); + return dma_acknowledge(); } return uint8_t(bus_.get_state()); case 1: -// LOG("[SCSI 1] Initiator command register get: " << (arbitration_in_progress_ ? 'p' : '-') << (lost_arbitration_ ? 'l' : '-')); + LOG("[1] Initiator command register get: " << (arbitration_in_progress_ ? 'p' : '-') << (lost_arbitration_ ? 'l' : '-')); return // Bits repeated as they were set. (initiator_command_ & ~0x60) | @@ -159,11 +168,11 @@ uint8_t NCR5380::read(int address, bool) { (lost_arbitration_ ? 0x20 : 0x00); case 2: -// LOG("[SCSI 2] Get mode"); + LOG("[2] Get mode"); return mode_; case 3: -// LOG("[SCSI 3] Get target command"); + LOG("[3] Get target command"); return target_command_; case 4: { @@ -177,41 +186,38 @@ uint8_t NCR5380::read(int address, bool) { ((bus_state & Line::Input) ? 0x04 : 0x00) | ((bus_state & Line::SelectTarget) ? 0x02 : 0x00) | ((bus_state & Line::Parity) ? 0x01 : 0x00); -// LOG("[SCSI 4] Get current bus state: " << PADHEX(2) << int(result)); + LOG("[4] Get current bus state: " << PADHEX(2) << int(result)); return result; } case 5: { const auto bus_state = bus_.get_state(); - const bool phase_matches = - (target_output() & (Line::Message | Line::Control | Line::Input)) == - (bus_state & (Line::Message | Line::Control | Line::Input)); - const uint8_t result = - /* b7 = end of DMA */ + (end_of_dma_ ? 0x80 : 0x00) | ((dma_request_ && state_ == ExecutionState::PerformingDMA) ? 0x40 : 0x00) | /* b5 = parity error */ - /* b4 = IRQ active */ - (phase_matches ? 0x08 : 0x00) | + (irq_ ? 0x10 : 0x00) | + (phase_matches() ? 0x08 : 0x00) | /* b2 = busy error */ ((bus_state & Line::Attention) ? 0x02 : 0x00) | ((bus_state & Line::Acknowledge) ? 0x01 : 0x00); -// LOG("[SCSI 5] Get bus and status: " << PADHEX(2) << int(result)); + LOG("[5] Get bus and status: " << PADHEX(2) << int(result)); return result; } case 6: -// LOG("[SCSI 6] Get input data"); + LOG("[6] Get input data"); return 0xff; case 7: -// LOG("[SCSI 7] Reset parity/interrupt"); + LOG("[7] Reset parity/interrupt"); + irq_ = false; return 0xff; } return 0; } -SCSI::BusState NCR5380::target_output() { +SCSI::BusState NCR5380::target_output() const { SCSI::BusState output = SCSI::DefaultBusState; if(target_command_ & 0x08) output |= Line::Request; if(target_command_ & 0x04) output |= Line::Message; @@ -236,6 +242,17 @@ void NCR5380::update_control_output() { } void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) { + /* + When connected as an Initiator with DMA Mode True, + if the phase lines I//O, C//D, and /MSG do not match the + phase bits in the Target Command Register, a phase mismatch + interrupt is generated when /REQ goes active. + */ + if((mode_ & 0x42) == 0x02 && new_state & SCSI::Line::Request && !phase_matches()) { + irq_ = true; + phase_mismatch_ = true; + } + switch(state_) { default: break; @@ -296,7 +313,13 @@ void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double dma_request_ = false; break; case SCSI::Line::Request: - dma_request_ = true; + // Don't issue a new DMA request if a phase mismatch has + // been detected and this is an intiator receiving. + // This is a bit of reading between the lines. + // (i.e. guesswork, partly) + dma_request_ = + !phase_mismatch_ || + (dma_operation_ != DMAOperation::InitiatorReceive); break; case SCSI::Line::Request | SCSI::Line::Acknowledge: dma_request_ = false; @@ -316,3 +339,38 @@ void NCR5380::set_execution_state(ExecutionState state) { state_ = state; if(state != ExecutionState::PerformingDMA) dma_operation_ = DMAOperation::Ready; } + +size_t NCR5380::scsi_id() { + return device_id_; +} + +bool NCR5380::dma_request() { + return dma_request_; +} + +uint8_t NCR5380::dma_acknowledge() { + const uint8_t bus_state = uint8_t(bus_.get_state()); + + dma_acknowledge_ = true; + dma_request_ = false; + update_control_output(); + bus_.set_device_output(device_id_, bus_output_); + + return bus_state; +} + +void NCR5380::dma_acknowledge(uint8_t value) { + data_bus_ = value; + + dma_acknowledge_ = true; + dma_request_ = false; + update_control_output(); + bus_.set_device_output(device_id_, bus_output_); +} + +bool NCR5380::phase_matches() const { + const auto bus_state = bus_.get_state(); + return + (target_output() & (Line::Message | Line::Control | Line::Input)) == + (bus_state & (Line::Message | Line::Control | Line::Input)); +} diff --git a/Components/5380/ncr5380.hpp b/Components/5380/ncr5380.hpp index 8e7874d38..6ea409b53 100644 --- a/Components/5380/ncr5380.hpp +++ b/Components/5380/ncr5380.hpp @@ -30,6 +30,18 @@ class NCR5380 final: public SCSI::Bus::Observer { /*! Reads from @c address. */ uint8_t read(int address, bool dma_acknowledge = false); + /*! @returns The SCSI ID assigned to this device. */ + size_t scsi_id(); + + /*! @return @c true if DMA request is active; @c false otherwise. */ + bool dma_request(); + + /*! Signals DMA acknowledge with a simultaneous read. */ + uint8_t dma_acknowledge(); + + /*! Signals DMA acknowledge with a simultaneous write. */ + void dma_acknowledge(uint8_t); + private: SCSI::Bus &bus_; @@ -46,6 +58,10 @@ class NCR5380 final: public SCSI::Bus::Observer { bool assert_data_bus_ = false; bool dma_request_ = false; bool dma_acknowledge_ = false; + bool end_of_dma_ = false; + + bool irq_ = false; + bool phase_mismatch_ = false; enum class ExecutionState { None, @@ -63,10 +79,11 @@ class NCR5380 final: public SCSI::Bus::Observer { void set_execution_state(ExecutionState state); - SCSI::BusState target_output(); + SCSI::BusState target_output() const; void update_control_output(); void scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) final; + bool phase_matches() const; }; } diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index e6d986ea8..d99f31147 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -24,16 +24,28 @@ #include "DiskIICard.hpp" #include "Joystick.hpp" #include "LanguageCardSwitches.hpp" +#include "SCSICard.hpp" #include "Video.hpp" #include "../../../Analyser/Static/AppleII/Target.hpp" #include "../../../ClockReceiver/ForceInline.hpp" #include "../../../Configurable/StandardOptions.hpp" +#include "../../../Storage/MassStorage/SCSI/SCSI.hpp" +#include "../../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp" +#include "../../../Storage/MassStorage/Encodings/MacintoshVolume.hpp" + #include #include #include +namespace { + +constexpr int DiskIISlot = 6; // Apple recommended slot 6 for the (first) Disk II. +constexpr int SCSISlot = 7; // Install the SCSI card in slot 7, to one-up any connected Disk II. + +} + namespace Apple { namespace II { @@ -101,7 +113,10 @@ template class ConcreteMachine: Cycles cycles_since_audio_update_; // MARK: - Cards - std::array, 7> cards_; + static constexpr size_t NoActiveCard = 7; // There is no 'card 0' in internal numbering. + size_t active_card_ = NoActiveCard; + + std::array, 8> cards_; // The final slot is a sentinel for 'no active card'. Cycles cycles_since_card_update_; std::vector every_cycle_cards_; std::vector just_in_time_cards_; @@ -141,7 +156,11 @@ template class ConcreteMachine: } Apple::II::DiskIICard *diskii_card() { - return dynamic_cast(cards_[5].get()); + return dynamic_cast(cards_[DiskIISlot - 1].get()); + } + + Apple::II::SCSICard *scsi_card() { + return dynamic_cast(cards_[SCSISlot - 1].get()); } // MARK: - Memory Map. @@ -460,10 +479,15 @@ template class ConcreteMachine: const bool has_disk_controller = target.disk_controller != Target::DiskController::None; const bool is_sixteen_sector = target.disk_controller == Target::DiskController::SixteenSector; if(has_disk_controller) { - // Apple recommended slot 6 for the (first) Disk II. request = request && DiskIICard::rom_request(is_sixteen_sector); } + // Add a SCSI card if requested. + const bool has_scsi_card = target.scsi_controller == Target::SCSIController::AppleSCSI; + if(has_scsi_card) { + request = request && SCSICard::rom_request(); + } + // Request, validate and install ROMs. auto roms = rom_fetcher(request); if(!request.validate(roms)) { @@ -471,7 +495,13 @@ template class ConcreteMachine: } if(has_disk_controller) { - install_card(6, new Apple::II::DiskIICard(roms, is_sixteen_sector)); + install_card(DiskIISlot, new Apple::II::DiskIICard(roms, is_sixteen_sector)); + } + + if(has_scsi_card) { + // Rounding the clock rate slightly shouldn't matter, but: + // TODO: be [slightly] more honest about clock rate. + install_card(SCSISlot, new Apple::II::SCSICard(roms, int(master_clock / 14.0f))); } rom_ = std::move(roms.find(system)->second); @@ -727,18 +757,32 @@ template class ConcreteMachine: Communication with cards follows. */ - if(!read_pages_[address >> 8] && address >= 0xc090 && address < 0xc800) { + if(!read_pages_[address >> 8] && address >= 0xc090 && address < 0xd000) { // If this is a card access, figure out which card is at play before determining // the totality of who needs messaging. size_t card_number = 0; Apple::II::Card::Select select = Apple::II::Card::None; - if(address >= 0xc100) { + if(address >= 0xc800) { + /* + Decode the 2kb area used for additional ROMs. + This is shared by all cards. + */ + card_number = active_card_; + select = Apple::II::Card::C8Region; + + // An access to $cfff will disable the active card. + if(address == 0xcfff) { + active_card_ = NoActiveCard; + } + } else if(address >= 0xc100) { /* Decode the area conventionally used by cards for ROMs: 0xCn00 to 0xCnff: card n. + + This also sets the active card for the C8 region. */ - card_number = (address - 0xc100) >> 8; + active_card_ = card_number = (address - 0xc100) >> 8; select = Apple::II::Card::Device; } else { /* @@ -863,6 +907,12 @@ template class ConcreteMachine: auto diskii = diskii_card(); if(diskii) diskii->set_disk(media.disks[0], 0); } + + if(!media.mass_storage_devices.empty()) { + auto scsi = scsi_card(); + if(scsi) scsi->set_storage_device(media.mass_storage_devices[0]); + } + return true; } diff --git a/Machines/Apple/AppleII/Card.hpp b/Machines/Apple/AppleII/Card.hpp index 692de839e..60c3bd519 100644 --- a/Machines/Apple/AppleII/Card.hpp +++ b/Machines/Apple/AppleII/Card.hpp @@ -42,9 +42,13 @@ class Card { public: virtual ~Card() {} enum Select: int { - None = 0, // No select line is active - IO = 1 << 0, // IO select is active - Device = 1 << 1, // Device select is active + None = 0, // No select line is active. + IO = 1 << 0, // IO select is active; i.e. access is in range $C0x0 to $C0xf. + Device = 1 << 1, // Device select is active; i.e. access is in range $Cx00 to $Cxff. + + C8Region = 1 << 2, // Access is to the region $c800 to $cfff, was preceded by at least + // one Device access to this card, and has not yet been followed up + // by an access to $cfff. }; /*! @@ -54,7 +58,7 @@ class Card { no constraints, that want to be informed of every machine cycle, will receive a call to perform_bus_operation every cycle and should use that for time keeping. */ - virtual void run_for([[maybe_unused]] Cycles half_cycles, [[maybe_unused]] int stretches) {} + virtual void run_for([[maybe_unused]] Cycles cycles, [[maybe_unused]] int stretches) {} /// Requests a flush of any pending audio or video output. virtual void flush() {} diff --git a/Machines/Apple/AppleII/SCSICard.cpp b/Machines/Apple/AppleII/SCSICard.cpp new file mode 100644 index 000000000..5d18adf3b --- /dev/null +++ b/Machines/Apple/AppleII/SCSICard.cpp @@ -0,0 +1,169 @@ +// +// SCSICard.cpp +// Clock Signal +// +// Created by Thomas Harte on 22/08/2022. +// Copyright © 2022 Thomas Harte. All rights reserved. +// + +// Per the documentation around the GGLabs Apple II SCSI card clone: +// +// A 5380 is mapped to the first eight bytes of slot IO: +// +// $c0x0 R current SCSI data register +// $c0x0 W output data register +// $c0x1 R/W initiator command register +// $c0x2 R/W mode select register +// $c0x3 R/W target command register +// $c0x4 R SCSI bus status +// $c0x4 W select enable register +// $c0x5 R bus and status register +// $c0x6 R input data register +// $c0x7 R reset parity and interrupts +// (i.e. the 5380's standard registers in their usual order) +// +// The remaining eight are used for control functions: +// +// $c0x8 R/W PDMA/DACK +// $c0x9 R SCSI device ID +// $c0xa W memory bank select register +// $c0xb W reset 5380 SCSI chip +// $c0xc - [unused] +// $c0xd W PDMA mode enable +// $c0xe R read DRQ status through bit 7 +// $c0xf - [unused] +// +// Further, per that card's schematic: +// +// BANK REGISTER: bit 0..3 ROM Addr, 4..6 RAM Addr, 7 RSVD +// +// Which relates to the description: +// +// The card is also equipped with 16K of ROM and 8K of RAM. +// These are mapped in the $C800-$CFFF card memory using a banking +// scheme. The $C0xA bank register selects the which bank of RAM +// and ROM are mapped. RAM is always at $C800-$CBFF and ROM is +// at $CC00-$CFFF. The boot code in the first 256 bytes of ROM +// bank 0 is also mapped in the IOSEL space ($Cn00-$CnFF). +// + +#include "SCSICard.hpp" + +#include + +using namespace Apple::II; + +ROM::Request SCSICard::rom_request() { + return ROM::Request(ROM::Name::AppleIISCSICard); +} + +// TODO: accept and supply real clock rate. +SCSICard::SCSICard(ROM::Map &map, int clock_rate) : + scsi_bus_(clock_rate), + ncr5380_(scsi_bus_, clock_rate), + storage_(scsi_bus_, 6) +{ + // Grab a copy of the SCSI ROM. + const auto rom = map.find(ROM::Name::AppleIISCSICard); + if(rom == map.end()) { + throw ROMMachine::Error::MissingROMs; + } + memcpy(rom_.data(), rom->second.data(), rom_.size()); + + // Set up initial banking. + rom_pointer_ = rom_.data(); + ram_pointer_ = ram_.data(); +} + +void SCSICard::perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) { + switch(select) { + default: break; + + case Select::Device: + if(is_read) { + *value = rom_[address & 255]; + } + break; + + case Select::IO: + address &= 0xf; + switch(address) { + case 0x0: case 0x1: case 0x2: case 0x3: + case 0x4: case 0x5: case 0x6: case 0x7: + if(is_read) { + *value = ncr5380_.read(address); + } else { + ncr5380_.write(address, *value); + } + break; + + case 0x9: + if(is_read) { + *value = uint8_t(ncr5380_.scsi_id()); + } + break; + + case 0x8: + // DMA acknowledge. + if(is_read) { + *value = ncr5380_.dma_acknowledge(); + } else { + ncr5380_.dma_acknowledge(*value); + } + break; + + case 0xa: + // RAM and ROM select. + if(!is_read) { + const auto rom_base = size_t((*value & 0x0f) << 10); + const auto ram_base = size_t((*value & 0x70) << 6); + + rom_pointer_ = &rom_[rom_base]; + ram_pointer_ = &ram_[ram_base]; + } + break; + + case 0xb: + if(!is_read) { + printf("TODO: NCR reset\n"); + } + break; + + case 0xd: + if(!is_read) { + printf("TODO: Enable PDMA\n"); + } + break; + + case 0xe: + // DRQ in b7. + if(is_read) { + *value = ncr5380_.dma_request() ? 0x80 : 0x00; + } + break; + + default: + printf("Unhandled: %04x %c %02x\n", address, is_read ? 'r' : 'w', *value); + break; + } + break; + + case Select::C8Region: + if(address & 0x400) { + if(is_read) { + *value = rom_pointer_[address & 0x3ff]; + } + } else { + if(is_read) { + *value = ram_pointer_[address & 0x3ff]; + } else { + ram_pointer_[address & 0x3ff] = *value; + } + } + break; + } +} + +void SCSICard::set_storage_device(const std::shared_ptr &device) { + storage_->set_storage(device); +} diff --git a/Machines/Apple/AppleII/SCSICard.hpp b/Machines/Apple/AppleII/SCSICard.hpp new file mode 100644 index 000000000..8ce7ce20d --- /dev/null +++ b/Machines/Apple/AppleII/SCSICard.hpp @@ -0,0 +1,55 @@ +// +// SCSICard.hpp +// Clock Signal +// +// Created by Thomas Harte on 22/08/2022. +// Copyright © 2022 Thomas Harte. All rights reserved. +// + +#ifndef SCSICard_hpp +#define SCSICard_hpp + +#include "Card.hpp" +#include "../../ROMMachine.hpp" + +#include "../../../Components/5380/ncr5380.hpp" + +#include "../../../Storage/MassStorage/SCSI/SCSI.hpp" +#include "../../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp" +#include "../../../Storage/MassStorage/MassStorageDevice.hpp" + +#include +#include + +namespace Apple { +namespace II { + +class SCSICard: public Card { + public: + static ROM::Request rom_request(); + SCSICard(ROM::Map &, int clock_rate); + + void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) final; + + void set_storage_device(const std::shared_ptr &device); + + void run_for([[maybe_unused]] Cycles cycles, [[maybe_unused]] int stretches) final { + scsi_bus_.run_for(cycles); + } + + private: + uint8_t *ram_pointer_ = nullptr; + uint8_t *rom_pointer_ = nullptr; + + std::array ram_; + std::array rom_; + + SCSI::Bus scsi_bus_; + NCR::NCR5380::NCR5380 ncr5380_; + SCSI::Target::Target storage_; +}; + +} +} + +#endif /* SCSICard_hpp */ diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 2326465de..35585c902 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -420,6 +420,9 @@ Description::Description(Name name) { case Name::AppleIIEnhancedECharacter: *this = Description(name, "AppleII", "the Enhanced Apple IIe character ROM", "apple2e-character.rom", 4*1024, 0x2651014du); break; + case Name::AppleIISCSICard: + *this = Description(name, "AppleII", "the Apple II SCSI card ROM", "scsi.rom", 16*1024, 0x5aff85d3u); + break; case Name::AppleIIgsROM00: /* TODO */ case Name::AppleIIgsROM01: *this = Description(name, "AppleIIgs", "the Apple IIgs ROM01", "apple2gs.rom", 128*1024, 0x42f124b0u); break; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index de370657b..6619710ef 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -58,6 +58,7 @@ enum Name { AppleIIeCharacter, AppleIIEnhancedE, AppleIIEnhancedECharacter, + AppleIISCSICard, // Apple IIgs. AppleIIgsROM00, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index e1c095f33..c7703df21 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -254,6 +254,10 @@ 4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; }; 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; + 4B4C81C528B3C5CD00F84AE9 /* SCSICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C81C328B3C5CD00F84AE9 /* SCSICard.cpp */; }; + 4B4C81C628B3C5CD00F84AE9 /* SCSICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C81C328B3C5CD00F84AE9 /* SCSICard.cpp */; }; + 4B4C81CA28B56CF800F84AE9 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C81C928B56CF800F84AE9 /* MacintoshVolume.cpp */; }; + 4B4C81CB28B56CF800F84AE9 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C81C928B56CF800F84AE9 /* MacintoshVolume.cpp */; }; 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; }; 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; }; 4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; }; @@ -312,8 +316,6 @@ 4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B71368F1F789C93008B8ED9 /* SegmentParser.cpp */; }; 4B74CF812312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; }; 4B74CF822312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; }; - 4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; }; - 4B74CF86231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; }; 4B75F979280D7C5100121055 /* 68000DecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B75F978280D7C5100121055 /* 68000DecoderTests.mm */; }; 4B75F97B280D7C7700121055 /* 68000 Decoding in Resources */ = {isa = PBXBuildFile; fileRef = 4B75F97A280D7C7700121055 /* 68000 Decoding */; }; 4B7752A628217DF80073E2C5 /* Dave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFEA2ED2682A7B900EBF94C /* Dave.cpp */; }; @@ -408,7 +410,6 @@ 4B778F2F23A5F0B10000D260 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; }; 4B778F3023A5F0C50000D260 /* Macintosh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE0058227CFFCA000CA200 /* Macintosh.cpp */; }; 4B778F3123A5F0CB0000D260 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */; }; - 4B778F3223A5F0EE0000D260 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; }; 4B778F3323A5F0FB0000D260 /* MassStorageDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6AAEA2230E3E1D0078E864 /* MassStorageDevice.cpp */; }; 4B778F3423A5F1040000D260 /* DirectAccessDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC890D1230F86020025A55A /* DirectAccessDevice.cpp */; }; 4B778F3523A5F1040000D260 /* SCSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6AAEA7230E40250078E864 /* SCSI.cpp */; }; @@ -1360,6 +1361,12 @@ 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KonamiSCC.cpp; sourceTree = ""; }; 4B4B1A3B200198C900A0F866 /* KonamiSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KonamiSCC.hpp; sourceTree = ""; }; 4B4C81C228B0288B00F84AE9 /* BlitterSequencer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BlitterSequencer.hpp; sourceTree = ""; }; + 4B4C81C328B3C5CD00F84AE9 /* SCSICard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SCSICard.cpp; sourceTree = ""; }; + 4B4C81C428B3C5CD00F84AE9 /* SCSICard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SCSICard.hpp; sourceTree = ""; }; + 4B4C81C828B56CF800F84AE9 /* MacintoshVolume.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MacintoshVolume.hpp; sourceTree = ""; }; + 4B4C81C928B56CF800F84AE9 /* MacintoshVolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MacintoshVolume.cpp; sourceTree = ""; }; + 4B4C81CC28B56DD400F84AE9 /* ApplePartitionMap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ApplePartitionMap.hpp; sourceTree = ""; }; + 4B4C81CD28B67FCD00F84AE9 /* AppleIIVolume.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleIIVolume.hpp; sourceTree = ""; }; 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Vic20.cpp; sourceTree = ""; }; 4B4DC8201D2C2425003C5BF8 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Vic20.hpp; sourceTree = ""; }; 4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = ""; }; @@ -1453,8 +1460,6 @@ 4B7136901F789C93008B8ED9 /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = ""; }; 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = HFV.hpp; sourceTree = ""; }; 4B74CF802312FA9C00500CE8 /* HFV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HFV.cpp; sourceTree = ""; }; - 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MacintoshVolume.cpp; path = Encodings/MacintoshVolume.cpp; sourceTree = ""; }; - 4B74CF84231370BC00500CE8 /* MacintoshVolume.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MacintoshVolume.hpp; path = Encodings/MacintoshVolume.hpp; sourceTree = ""; }; 4B75F978280D7C5100121055 /* 68000DecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000DecoderTests.mm; sourceTree = ""; }; 4B75F97A280D7C7700121055 /* 68000 Decoding */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "68000 Decoding"; sourceTree = ""; }; 4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Z80.hpp; sourceTree = ""; }; @@ -2917,6 +2922,17 @@ path = KonamiSCC; sourceTree = ""; }; + 4B4C81C728B56CF800F84AE9 /* Encodings */ = { + isa = PBXGroup; + children = ( + 4B4C81C928B56CF800F84AE9 /* MacintoshVolume.cpp */, + 4B4C81CC28B56DD400F84AE9 /* ApplePartitionMap.hpp */, + 4B4C81C828B56CF800F84AE9 /* MacintoshVolume.hpp */, + 4B4C81CD28B67FCD00F84AE9 /* AppleIIVolume.hpp */, + ); + path = Encodings; + sourceTree = ""; + }; 4B4DC81D1D2C2425003C5BF8 /* Commodore */ = { isa = PBXGroup; children = ( @@ -3145,10 +3161,9 @@ 4B6AAEA1230E3E1D0078E864 /* MassStorage */ = { isa = PBXGroup; children = ( - 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */, 4B6AAEA2230E3E1D0078E864 /* MassStorageDevice.cpp */, - 4B74CF84231370BC00500CE8 /* MacintoshVolume.hpp */, 4B6AAEA3230E3E1D0078E864 /* MassStorageDevice.hpp */, + 4B4C81C728B56CF800F84AE9 /* Encodings */, 4B74CF7E2312FA9C00500CE8 /* Formats */, 4B6AAEA5230E40250078E864 /* SCSI */, ); @@ -4617,6 +4632,7 @@ 4BCE0050227CE8CA000CA200 /* AppleII.cpp */, 4BCE004E227CE8CA000CA200 /* DiskIICard.cpp */, 4B2E86E025DC95150024F1E9 /* Joystick.cpp */, + 4B4C81C328B3C5CD00F84AE9 /* SCSICard.cpp */, 4BCE004D227CE8CA000CA200 /* Video.cpp */, 4BCE004A227CE8CA000CA200 /* AppleII.hpp */, 4BF40A5A254263140033EA39 /* AuxiliaryMemorySwitches.hpp */, @@ -4625,6 +4641,7 @@ 4B2E86E125DC95150024F1E9 /* Joystick.hpp */, 4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */, 4BE0151C286A8C8E00EA42E9 /* MemorySwitches.hpp */, + 4B4C81C428B3C5CD00F84AE9 /* SCSICard.hpp */, 4BCE004F227CE8CA000CA200 /* Video.hpp */, 4B8DF4F2254E141700F3433C /* VideoSwitches.hpp */, ); @@ -5581,6 +5598,7 @@ 4B055AB31FAE860F0060FFFF /* CSW.cpp in Sources */, 4B89451D201967B4007DE474 /* Disk.cpp in Sources */, 4BFEA2F02682A7B900EBF94C /* Dave.cpp in Sources */, + 4B4C81C628B3C5CD00F84AE9 /* SCSICard.cpp in Sources */, 4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */, 4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */, 4B7962A22819681F008130F9 /* Decoder.cpp in Sources */, @@ -5629,6 +5647,7 @@ 4B2E86B825D7490E0024F1E9 /* ReactiveDevice.cpp in Sources */, 4B74CF822312FA9C00500CE8 /* HFV.cpp in Sources */, 4B8318B022D3E531006DB630 /* AppleII.cpp in Sources */, + 4B4C81CB28B56CF800F84AE9 /* MacintoshVolume.cpp in Sources */, 4B1B58F7246CC4E8009C171E /* State.cpp in Sources */, 4B0ACC03237756F6008902D0 /* Line.cpp in Sources */, 4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */, @@ -5680,7 +5699,6 @@ 4BC6236F26F426B400F83DFE /* FAT.cpp in Sources */, 4B0F1BFD260300D900B85C66 /* ZXSpectrum.cpp in Sources */, 4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */, - 4B74CF86231370BC00500CE8 /* MacintoshVolume.cpp in Sources */, 4B0ACC3323775819008902D0 /* Atari2600.cpp in Sources */, 4BD424E02193B5340097291A /* TextureTarget.cpp in Sources */, 4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */, @@ -5757,6 +5775,7 @@ 4B8DF505254E3C9D00F3433C /* ADB.cpp in Sources */, 4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */, 4B0ACC2623775819008902D0 /* AtariST.cpp in Sources */, + 4B4C81C528B3C5CD00F84AE9 /* SCSICard.cpp in Sources */, 4B894530201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */, 4B1B88BB202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */, @@ -5829,7 +5848,6 @@ 4BCE0060227D39AB000CA200 /* Video.cpp in Sources */, 4B0ACC2E23775819008902D0 /* TIA.cpp in Sources */, 4B2E86B725D7490E0024F1E9 /* ReactiveDevice.cpp in Sources */, - 4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */, 4B0F1C232605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */, 4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */, 4B7C681A275196E8001671EC /* MouseJoystick.cpp in Sources */, @@ -5906,6 +5924,7 @@ 4B47770B268FBE4D005C2340 /* FAT.cpp in Sources */, 4B894528201967B4007DE474 /* Disk.cpp in Sources */, 4B2E86CF25D8D8C70024F1E9 /* Keyboard.cpp in Sources */, + 4B4C81CA28B56CF800F84AE9 /* MacintoshVolume.cpp in Sources */, 4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */, 4B7C681627517A59001671EC /* Sprites.cpp in Sources */, 4B0ACC02237756ED008902D0 /* Line.cpp in Sources */, @@ -6171,7 +6190,6 @@ 4B778EF523A5DB440000D260 /* StaticAnalyser.cpp in Sources */, 4BEE1EC022B5E236000A26A6 /* MacGCRTests.mm in Sources */, 4B778F0623A5EC150000D260 /* CAS.cpp in Sources */, - 4B778F3223A5F0EE0000D260 /* MacintoshVolume.cpp in Sources */, 4B778F2B23A5EF0F0000D260 /* Commodore.cpp in Sources */, 4B778F3F23A5F1890000D260 /* MacintoshDoubleDensityDrive.cpp in Sources */, 4B778F1623A5ECA00000D260 /* Z80AllRAM.cpp in Sources */, diff --git a/ROMImages/AppleII/readme.txt b/ROMImages/AppleII/readme.txt index 75c375f24..369aa8d70 100644 --- a/ROMImages/AppleII/readme.txt +++ b/ROMImages/AppleII/readme.txt @@ -11,4 +11,6 @@ apple2-character.rom — a 2kb image of the Apple IIe's character ROM. apple2eu-character.rom — a 4kb image of the Unenhanced IIe's character ROM. apple2e-character.rom — a 4kb image of the Enhanced IIe's character ROM. +scsi.rom — a 16kb image of the Apple II SCSI card ROM. + Apologies for the wackiness around "at least xkb big", it's to allow for use of files such as those on ftp.apple.asimov.net, which tend to be a bunch of other things, then the system ROM. \ No newline at end of file diff --git a/Storage/Disk/DiskImage/Formats/2MG.cpp b/Storage/Disk/DiskImage/Formats/2MG.cpp index 3defec745..c2894f4dd 100644 --- a/Storage/Disk/DiskImage/Formats/2MG.cpp +++ b/Storage/Disk/DiskImage/Formats/2MG.cpp @@ -9,12 +9,83 @@ #include "2MG.hpp" #include "MacintoshIMG.hpp" +#include "../../../MassStorage/Encodings/AppleIIVolume.hpp" + #include using namespace Storage::Disk; -DiskImageHolderBase *Disk2MG::open(const std::string &file_name) { +namespace { + +// TODO: I've boxed myself into a corner on this stuff by not using factories more generally; +// volume to device mappers are not themselves mass storage devices because then their use +// can't currently be private to file types and relevant knowledge would need to be pushed up into +// the static analyser. +// +// So, I guess: go factory, pervasively. And probably stop the strict disk/mass storage/tape +// distinction, given that clearly some platforms just capture volumes abstractly from media. +class MassStorage2MG: public Storage::MassStorage::MassStorageDevice { + public: + MassStorage2MG(const std::string &file_name, long start, long size): + file_(file_name), + file_start_(start), + image_size_(size) + { + mapper_.set_drive_type( + Storage::MassStorage::Encodings::Apple::DriveType::SCSI, + size_t(size / 512) + ); + } + + private: + Storage::FileHolder file_; + long file_start_, image_size_; + Storage::MassStorage::Encodings::AppleII::Mapper mapper_; + + /* MassStorageDevices overrides. */ + size_t get_block_size() final { + return 512; + } + size_t get_number_of_blocks() final { + return mapper_.get_number_of_blocks(); + } + std::vector get_block(size_t address) final { + const auto source_address = mapper_.to_source_address(address); + const auto file_offset = offset_for_block(source_address); + + if(source_address >= 0) { + file_.seek(file_offset, SEEK_SET); + return mapper_.convert_source_block(source_address, file_.read(get_block_size())); + } else { + return mapper_.convert_source_block(source_address); + } + } + void set_block(size_t address, const std::vector &data) final { + const auto source_address = mapper_.to_source_address(address); + const auto file_offset = offset_for_block(source_address); + + if(source_address >= 0 && file_offset >= 0) { + file_.seek(file_offset, SEEK_SET); + file_.write(data); + } + } + + /// @returns -1 if @c address is out of range; the offset into the file at which + /// the block for @c address resides otherwise. + long offset_for_block(ssize_t address) { + if(address < 0) return -1; + + const long offset = 512 * address; + if(offset > image_size_ - 512) return -1; + + return file_start_ + offset; + } +}; + +} + +Disk2MG::DiskOrMassStorageDevice Disk2MG::open(const std::string &file_name) { FileHolder file(file_name); // Check the signature. @@ -65,12 +136,16 @@ DiskImageHolderBase *Disk2MG::open(const std::string &file_name) { // TODO: DOS 3.3 sector order. break; case 1: - // ProDOS order, which could still mean Macintosh-style or Apple II-style. Try them both. + // 'ProDOS order', which could still mean Macintosh-style (ie. not ProDOS, but whatever) + // or Apple II-style. Try them both. try { return new DiskImageHolder(file_name, MacintoshIMG::FixedType::GCR, data_start, data_size); } catch(...) {} // TODO: Apple II-style. + + // Try a hard-disk image. For now this assumes: for an Apple IIe or GS. + return new MassStorage2MG(file_name, data_start, data_size); break; case 2: // TODO: NIB data (yuck!). diff --git a/Storage/Disk/DiskImage/Formats/2MG.hpp b/Storage/Disk/DiskImage/Formats/2MG.hpp index 8b37112a2..8b512e189 100644 --- a/Storage/Disk/DiskImage/Formats/2MG.hpp +++ b/Storage/Disk/DiskImage/Formats/2MG.hpp @@ -10,8 +10,12 @@ #define _MG_hpp #include "../DiskImage.hpp" +#include "../../../MassStorage/MassStorageDevice.hpp" + #include "../../../FileHolder.hpp" +#include + namespace Storage { namespace Disk { @@ -26,7 +30,8 @@ namespace Disk { class Disk2MG { public: - static DiskImageHolderBase *open(const std::string &file_name); + using DiskOrMassStorageDevice = std::variant; + static DiskOrMassStorageDevice open(const std::string &file_name); }; } diff --git a/Storage/MassStorage/Encodings/AppleIIVolume.hpp b/Storage/MassStorage/Encodings/AppleIIVolume.hpp new file mode 100644 index 000000000..10d921d03 --- /dev/null +++ b/Storage/MassStorage/Encodings/AppleIIVolume.hpp @@ -0,0 +1,38 @@ +// +// AppleIIVolume.hpp +// Clock Signal +// +// Created by Thomas Harte on 24/08/2022. +// Copyright © 2022 Thomas Harte. All rights reserved. +// + +#ifndef AppleIIVolume_h +#define AppleIIVolume_h + +#include "ApplePartitionMap.hpp" + +namespace Storage { +namespace MassStorage { +namespace Encodings { +namespace AppleII { + +struct VolumeProvider { + static constexpr bool HasDriver = false; + + const char *name() const { + return "ProDOS"; + } + + const char *type() const { + return "Apple_PRODOS"; + } +}; + +using Mapper = Storage::MassStorage::Encodings::Apple::PartitionMap; + +} +} +} +} + +#endif /* AppleIIVolume_h */ diff --git a/Storage/MassStorage/Encodings/ApplePartitionMap.hpp b/Storage/MassStorage/Encodings/ApplePartitionMap.hpp new file mode 100644 index 000000000..846ddba28 --- /dev/null +++ b/Storage/MassStorage/Encodings/ApplePartitionMap.hpp @@ -0,0 +1,257 @@ +// +// ApplePartitionMap.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/08/2022. +// Copyright © 2022 Thomas Harte. All rights reserved. +// + +#ifndef ApplePartitionMap_hpp +#define ApplePartitionMap_hpp + +#include +#include +#include +#include +#include + +namespace Storage { +namespace MassStorage { +namespace Encodings { +namespace Apple { + +enum class DriveType { + SCSI +}; + +/*! + Implements a device to volume mapping with an Apple Partition Map. + + The @c VolumeProvider provides both the volume to be embedded into a device, + and device driver information if applicable. +*/ +template class PartitionMap { + public: + /*! + Sets the drive type to map to and the number of blocks in the underlying partition. + */ + void set_drive_type(DriveType drive_type, size_t number_of_blocks) { + drive_type_ = drive_type; + number_of_blocks_ = number_of_blocks; + } + + /*! + @returns The total number of blocks on the entire volume. + */ + size_t get_number_of_blocks() const { + return + number_of_blocks_ + // Size of the volume. + size_t(non_volume_blocks()); // Size of everything else. + } + + /*! + Maps from a mass-storage device address to an address + in the underlying volume. + */ + ssize_t to_source_address(size_t address) const { + // The embedded volume is always the last thing on the device. + return ssize_t(address) - ssize_t(non_volume_blocks()); + } + + /*! + Converts from a source data block to one properly encoded for the drive type. + + Expected usage: + + const size_t source_address = mapper.to_source_address(unit_address); + if(is_in_range_for_partition(source_address)) { + return mapper.convert_source_block(source_address, get_block_contents(source_address)); + } else { + return mapper.convert_source_block(source_address); + } + */ + std::vector convert_source_block(ssize_t source_address, std::vector source_data = {}) { + // Addresses greater than or equal to zero map to the actual disk image. + if(source_address >= 0) return source_data; + + // Switch to mapping relative to 0, for personal sanity. + source_address += non_volume_blocks(); + + // Block 0 is the device descriptor, which lists the total number of blocks, + // and provides an offset to the driver, if any. + if(!source_address) { + const uint32_t total_device_blocks = uint32_t(get_number_of_blocks()); + const auto driver_size = uint16_t(driver_block_size()); + const auto driver_offset = uint16_t(predriver_blocks()); + const uint8_t driver_count = driver_size > 0 ? 1 : 0; + + /* The driver descriptor. */ + std::vector driver_description = { + 0x45, 0x52, /* device signature */ + 0x02, 0x00, /* block size, in bytes */ + + uint8_t(total_device_blocks >> 24), + uint8_t(total_device_blocks >> 16), + uint8_t(total_device_blocks >> 8), + uint8_t(total_device_blocks), + /* number of blocks on device */ + + 0x00, 0x01, /* reserved (formerly: device type) */ + 0x00, 0x01, /* reserved (formerly: device ID) */ + 0x00, 0x00, + 0x00, 0x00, /* reserved ('sbData', no further explanation given) */ + + 0x00, driver_count, /* number of device descriptor entries */ + 0x00, 0x00, + + /* first device descriptor's starting block */ + uint8_t(driver_offset >> 8), uint8_t(driver_offset), + + /* size of device driver */ + uint8_t(driver_size >> 8), uint8_t(driver_size), + + 0x00, 0x01, /* + More modern documentation: operating system (MacOS = 1) + Inside Macintosh IV: system type (Mac Plus = 1) + */ + }; + driver_description.resize(512); + + return driver_description; + } + + // Blocks 1 and 2 contain entries of the partition map; there's also possibly an entry + // for the driver. + if(source_address < 3 + volume_provider_.HasDriver) { + struct Partition { + const char *name, *type; + uint32_t start_block, size; + uint8_t status; + } partitions[3] = { + { + volume_provider_.name(), + volume_provider_.type(), + uint32_t(non_volume_blocks()), + uint32_t(number_of_blocks_), + 0xb7 + }, + { + "Apple", + "Apple_partition_map", + 0x01, + uint32_t(predriver_blocks()) - 1, + 0x37 + }, + { + "Macintosh", + "Apple_Driver", + uint32_t(predriver_blocks()), + uint32_t(driver_block_size()), + 0x7f + }, + }; + + std::vector partition(512); + + // Fill in the fixed fields. + partition[0] = 'P'; partition[1] = 'M'; /* Signature. */ + partition[7] = 3; /* Number of partitions. */ + + const Partition &details = partitions[source_address-1]; + + partition[8] = uint8_t(details.start_block >> 24); + partition[9] = uint8_t(details.start_block >> 16); + partition[10] = uint8_t(details.start_block >> 8); + partition[11] = uint8_t(details.start_block); + + partition[84] = partition[12] = uint8_t(details.size >> 24); + partition[85] = partition[13] = uint8_t(details.size >> 16); + partition[86] = partition[14] = uint8_t(details.size >> 8); + partition[87] = partition[15] = uint8_t(details.size); + + // 32 bytes are allocated for each of the following strings. + memcpy(&partition[16], details.name, strlen(details.name)); + memcpy(&partition[48], details.type, strlen(details.type)); + + partition[91] = details.status; + + // The third entry in this constructed partition map is the driver; + // add some additional details. + if constexpr (VolumeProvider::HasDriver) { + if(source_address == 3) { + const auto driver_size = uint16_t(volume_provider_.driver_size()); + const auto driver_checksum = uint16_t(volume_provider_.driver_checksum()); + + /* Driver size in bytes. */ + partition[98] = uint8_t(driver_size >> 8); + partition[99] = uint8_t(driver_size); + + /* Driver checksum. */ + partition[118] = uint8_t(driver_checksum >> 8); + partition[119] = uint8_t(driver_checksum); + + /* Driver target processor. */ + const char *driver_target = volume_provider_.driver_target(); + memcpy(&partition[120], driver_target, strlen(driver_target)); + + // Various non-zero values that Apple HD SC Tool wrote are below; they are + // documented as reserved officially, so I don't know their meaning. + partition[137] = 0x01; + partition[138] = 0x06; + partition[143] = 0x01; + partition[147] = 0x02; + partition[149] = 0x07; + } + } + + return partition; + } + + if constexpr (VolumeProvider::HasDriver) { + // The remainder of the non-volume area is the driver. + if(source_address >= predriver_blocks() && source_address < non_volume_blocks()) { + const uint8_t *driver = volume_provider_.driver(); + const auto offset = (source_address - predriver_blocks()) * 512; + return std::vector(&driver[offset], &driver[offset + 512]); + } + } + + // Default: return an empty block. + return std::vector(512); + } + + private: + DriveType drive_type_; + size_t number_of_blocks_; + + VolumeProvider volume_provider_; + + ssize_t predriver_blocks() const { + return + 0x40; // Holding: + // (i) the driver descriptor; + // (ii) the partition table; and + // (iii) the partition entries. + } + + ssize_t non_volume_blocks() const { + return + predriver_blocks() + + driver_block_size(); // Size of device driver (if any). + } + + ssize_t driver_block_size() const { + if constexpr (VolumeProvider::HasDriver) { + return (volume_provider_.driver_size() + 511) >> 9; + } else { + return 0; + } + } +}; + +} +} +} +} + +#endif /* ApplePartitionMap_hpp */ diff --git a/Storage/MassStorage/Encodings/MacintoshVolume.cpp b/Storage/MassStorage/Encodings/MacintoshVolume.cpp index 4c3ca0893..93b5e3e3b 100644 --- a/Storage/MassStorage/Encodings/MacintoshVolume.cpp +++ b/Storage/MassStorage/Encodings/MacintoshVolume.cpp @@ -12,290 +12,189 @@ using namespace Storage::MassStorage::Encodings::Macintosh; -void Mapper::set_drive_type(DriveType drive_type, size_t number_of_blocks) { - drive_type_ = drive_type; - number_of_blocks_ = number_of_blocks; +size_t VolumeProvider::driver_size() const { + return 0x139e; } -size_t Mapper::get_number_of_blocks() { - return number_of_blocks_ + 0x60; +uint16_t VolumeProvider::driver_checksum() const { + return 0x84b9; } -ssize_t Mapper::to_source_address(size_t address) { - return ssize_t(address) - 0x60; +const uint8_t *VolumeProvider::driver() const { + static constexpr uint8_t driver[] = { + 0x60, 0x00, 0x01, 0xba, 0x23, 0x24, 0x00, 0x01, 0x00, 0x27, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x13, 0x9e, 0x00, 0x00, 0xda, 0xe7, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, + 0x00, 0x7c, 0x00, 0x68, 0x00, 0x72, 0x00, 0x5e, 0x07, 0x2e, 0x53, 0x43, 0x53, 0x49, 0x30, 0x30, 0x41, 0xbd, 0x30, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x1f, 0x60, 0x00, + 0x02, 0x12, 0x41, 0xfa, 0xff, 0xf4, 0x20, 0xaf, 0x00, 0x04, 0x4e, 0x75, 0x20, 0x3a, 0xff, 0xea, 0x4e, 0x75, 0x20, 0x0d, 0x2a, 0x7a, 0xff, 0xe2, 0x4e, 0x75, 0x41, 0xfa, 0xff, 0xd8, 0x30, 0xaf, + 0x00, 0x06, 0x4e, 0x75, 0x48, 0x7a, 0x00, 0x4e, 0x20, 0x1f, 0x4e, 0x75, 0x70, 0x00, 0x31, 0x40, 0x00, 0x10, 0x60, 0x2a, 0x2f, 0x08, 0x2f, 0x09, 0x4e, 0xba, 0x05, 0x4a, 0x60, 0x1c, 0x2f, 0x08, + 0x2f, 0x09, 0x4e, 0xba, 0x07, 0x82, 0x60, 0x12, 0x2f, 0x08, 0x2f, 0x09, 0x4e, 0xba, 0x08, 0xca, 0x60, 0x08, 0x2f, 0x08, 0x2f, 0x09, 0x4e, 0xba, 0x05, 0xf2, 0x22, 0x5f, 0x20, 0x5f, 0x4a, 0x40, + 0x67, 0x04, 0x31, 0xc0, 0x01, 0x42, 0x08, 0x28, 0x00, 0x09, 0x00, 0x06, 0x66, 0x04, 0x2f, 0x38, 0x08, 0xfc, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, + 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x01, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x53, 0x43, 0x53, 0x49, 0x20, 0x30, 0x00, 0x48, 0xe7, 0x3f, 0x3e, + 0x49, 0xfa, 0xfe, 0x50, 0x26, 0x14, 0x42, 0x94, 0x70, 0x00, 0x42, 0x42, 0x28, 0x3a, 0xfe, 0x40, 0x53, 0x44, 0x49, 0xfa, 0xfe, 0x2c, 0x14, 0x1c, 0xd0, 0x42, 0xe3, 0x58, 0x51, 0xcc, 0xff, 0xf8, + 0x4a, 0x40, 0x66, 0x02, 0x53, 0x40, 0x49, 0xfa, 0xfe, 0x2a, 0x28, 0x80, 0xb6, 0x80, 0x67, 0x0e, 0x49, 0xfa, 0xfe, 0x20, 0x38, 0x83, 0x4c, 0xdf, 0x7c, 0xfc, 0x70, 0xff, 0x4e, 0x75, 0x28, 0x48, + 0x41, 0xfa, 0xfd, 0xfe, 0x20, 0x3a, 0xfe, 0x08, 0xa0, 0x20, 0x70, 0x06, 0x22, 0x78, 0x01, 0x1c, 0xd2, 0xfc, 0x00, 0x9c, 0x24, 0x21, 0x67, 0x1e, 0x24, 0x42, 0x20, 0x52, 0x24, 0x50, 0x0c, 0xaa, + 0x41, 0xbd, 0x30, 0xb0, 0x00, 0x1a, 0x66, 0x0e, 0xb6, 0xaa, 0xff, 0xfc, 0x66, 0x08, 0x41, 0xfa, 0xfd, 0xd0, 0x4e, 0xea, 0x00, 0x26, 0x51, 0xc8, 0xff, 0xdc, 0x4a, 0x85, 0x6b, 0x14, 0x20, 0x78, + 0x02, 0xa6, 0x20, 0x50, 0xd0, 0xfc, 0x1f, 0x40, 0x2f, 0x38, 0x01, 0x18, 0xa0, 0x57, 0x21, 0xdf, 0x01, 0x18, 0x70, 0x20, 0xd0, 0x45, 0x38, 0x00, 0x46, 0x40, 0xa0, 0x3d, 0xe5, 0x44, 0x22, 0x78, + 0x01, 0x1c, 0x20, 0x71, 0x40, 0x00, 0x2c, 0x50, 0xa0, 0x29, 0x41, 0xfa, 0xfd, 0xaa, 0x2c, 0x88, 0x3d, 0x58, 0x00, 0x04, 0x2d, 0x58, 0x00, 0x22, 0x3d, 0x58, 0x00, 0x26, 0x08, 0xee, 0x00, 0x05, + 0x00, 0x05, 0x08, 0xae, 0x00, 0x06, 0x00, 0x05, 0x70, 0x01, 0x08, 0x05, 0x00, 0x1e, 0x67, 0x02, 0x70, 0x00, 0x2f, 0x00, 0x2f, 0x0e, 0x4e, 0xba, 0x00, 0x3c, 0x50, 0x8f, 0x4a, 0x40, 0x67, 0x02, + 0x70, 0xe9, 0x4c, 0xdf, 0x7c, 0xfc, 0x4e, 0x75, 0x2f, 0x02, 0x42, 0x67, 0x2f, 0x2f, 0x00, 0x0a, 0x3f, 0x3c, 0x00, 0x01, 0x3f, 0x3c, 0x00, 0x03, 0xa8, 0x95, 0x54, 0x8f, 0x24, 0x1f, 0x4e, 0x75, + 0x2f, 0x02, 0x42, 0x67, 0x2f, 0x2f, 0x00, 0x0a, 0x3f, 0x3c, 0x00, 0x04, 0xa8, 0x95, 0x54, 0x8f, 0x24, 0x1f, 0x4e, 0x75, 0x4e, 0x56, 0xfd, 0xf4, 0x48, 0xe7, 0x3e, 0x30, 0x26, 0x6e, 0x00, 0x08, + 0x24, 0x2e, 0x00, 0x0c, 0x4e, 0xba, 0xfd, 0x66, 0x4a, 0x80, 0x67, 0x0c, 0x4e, 0xba, 0xfd, 0x64, 0x2d, 0x40, 0xff, 0xf6, 0x60, 0x00, 0x00, 0x9a, 0x4e, 0xba, 0x0e, 0xde, 0x28, 0x00, 0x4e, 0xba, + 0x0e, 0xe8, 0x2f, 0x00, 0x4e, 0xba, 0x0e, 0xd8, 0x4e, 0xba, 0x0f, 0x92, 0x26, 0x00, 0x2f, 0x04, 0x4e, 0xba, 0x0e, 0xcc, 0x4a, 0x83, 0x50, 0x8f, 0x66, 0x06, 0x70, 0xff, 0x60, 0x00, 0x02, 0x9c, + 0x2f, 0x03, 0x4e, 0xba, 0x0f, 0x48, 0x2f, 0x03, 0x4e, 0xba, 0xfd, 0x18, 0x4e, 0xba, 0xfd, 0x24, 0x2d, 0x40, 0xff, 0xf6, 0x20, 0x38, 0x02, 0xae, 0x50, 0x80, 0x24, 0x40, 0x1b, 0x52, 0xfc, 0x70, + 0x50, 0x8f, 0x66, 0x06, 0x42, 0x2d, 0xfc, 0x74, 0x60, 0x14, 0x1b, 0x7c, 0x00, 0x01, 0xfc, 0x74, 0x0c, 0x2d, 0x00, 0x03, 0xfc, 0x70, 0x65, 0x06, 0x1b, 0x7c, 0x00, 0x02, 0xfc, 0x70, 0x42, 0x6d, + 0xfc, 0x88, 0x42, 0xa7, 0x4e, 0xba, 0xfc, 0xf4, 0x42, 0x6d, 0xfc, 0x8c, 0x42, 0x2d, 0xfc, 0x78, 0x78, 0x00, 0x58, 0x8f, 0x60, 0x0e, 0x20, 0x04, 0xd0, 0x80, 0x41, 0xed, 0xfc, 0x90, 0x42, 0x70, + 0x08, 0x00, 0x52, 0x84, 0x70, 0x08, 0xb0, 0x84, 0x62, 0x00, 0xff, 0xec, 0x42, 0x2d, 0xfc, 0x7c, 0x36, 0x2b, 0x00, 0x18, 0x46, 0x43, 0x04, 0x43, 0x00, 0x20, 0x72, 0x00, 0x32, 0x03, 0x20, 0x01, + 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x45, 0xed, 0xfc, 0xa0, 0xd5, 0xc1, 0x4e, 0xba, 0xfc, 0x94, 0x27, 0x40, 0x00, 0x14, 0x42, 0xa7, + 0x70, 0x00, 0x30, 0x03, 0x2f, 0x00, 0x4e, 0xba, 0x07, 0x62, 0x42, 0x46, 0x42, 0x6e, 0xff, 0xfe, 0x7a, 0x01, 0x2d, 0x45, 0xff, 0xfa, 0x78, 0x01, 0x50, 0x8f, 0x60, 0x00, 0x01, 0x50, 0x48, 0x6e, + 0xfd, 0xf6, 0x2f, 0x04, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x70, 0x00, 0x30, 0x03, 0x2f, 0x00, 0x4e, 0xba, 0x08, 0x58, 0x3a, 0x00, 0x4f, 0xef, 0x00, 0x14, 0x67, 0x08, 0x0c, 0x45, 0xff, 0xfc, + 0x66, 0x00, 0x01, 0x28, 0x0c, 0x6e, 0x50, 0x4d, 0xfd, 0xf6, 0x66, 0x00, 0x01, 0x1e, 0x70, 0x01, 0xb0, 0x84, 0x66, 0x06, 0x2d, 0x6e, 0xfd, 0xfa, 0xff, 0xfa, 0x4a, 0x6e, 0xff, 0xfe, 0x66, 0x4e, + 0x41, 0xee, 0xfe, 0x26, 0x0c, 0x90, 0x41, 0x70, 0x70, 0x6c, 0x66, 0x42, 0x41, 0xee, 0xfe, 0x2a, 0x0c, 0x90, 0x65, 0x5f, 0x44, 0x72, 0x66, 0x36, 0x0c, 0xae, 0x00, 0x01, 0x06, 0x00, 0xfe, 0x7e, + 0x66, 0x2c, 0x72, 0x01, 0x3d, 0x41, 0xff, 0xfe, 0x70, 0x00, 0x30, 0x03, 0xd0, 0x80, 0x41, 0xed, 0xfc, 0x90, 0x31, 0xae, 0xfe, 0x8c, 0x08, 0x00, 0x35, 0x6e, 0xfe, 0x84, 0x00, 0x2c, 0x15, 0x6e, + 0xfe, 0x89, 0x00, 0x25, 0x15, 0x6e, 0xfe, 0x8b, 0x00, 0x27, 0x60, 0x00, 0x00, 0xbe, 0x4a, 0x46, 0x66, 0x00, 0x00, 0xb8, 0x41, 0xee, 0xfe, 0x26, 0x0c, 0x90, 0x41, 0x70, 0x70, 0x6c, 0x66, 0x00, + 0x00, 0xaa, 0x41, 0xee, 0xfe, 0x2a, 0x0c, 0x90, 0x65, 0x5f, 0x48, 0x46, 0x66, 0x00, 0x00, 0x9c, 0x7c, 0x01, 0x7a, 0x08, 0x20, 0x78, 0x03, 0x0a, 0x60, 0x0e, 0x30, 0x28, 0x00, 0x06, 0xb0, 0x45, + 0x66, 0x04, 0x52, 0x45, 0x60, 0xee, 0x20, 0x50, 0x20, 0x08, 0x66, 0x00, 0xff, 0xee, 0x42, 0x52, 0x08, 0x2e, 0x00, 0x05, 0xfe, 0x51, 0x67, 0x04, 0x70, 0x00, 0x60, 0x06, 0x20, 0x3c, 0x00, 0x00, + 0x00, 0x80, 0x15, 0x40, 0x00, 0x02, 0x15, 0x7c, 0x00, 0x08, 0x00, 0x03, 0x15, 0x7c, 0x00, 0x01, 0x00, 0x04, 0x42, 0x2a, 0x00, 0x05, 0x35, 0x7c, 0x00, 0x01, 0x00, 0x0a, 0x42, 0xaa, 0x00, 0x06, + 0x42, 0x6a, 0x00, 0x10, 0x35, 0x6e, 0xfe, 0x4c, 0x00, 0x12, 0x20, 0x2e, 0xfe, 0x4a, 0x72, 0x10, 0xe2, 0xa8, 0x35, 0x40, 0x00, 0x14, 0x20, 0x2e, 0xfe, 0x46, 0xd0, 0xae, 0xfd, 0xfe, 0x25, 0x40, + 0x00, 0x18, 0x25, 0x40, 0x00, 0x1c, 0x25, 0x6e, 0xfe, 0x4a, 0x00, 0x20, 0x35, 0x45, 0x00, 0x16, 0x48, 0x6a, 0x00, 0x06, 0x70, 0x00, 0x30, 0x05, 0x2f, 0x00, 0x30, 0x2b, 0x00, 0x18, 0x48, 0xc0, + 0x2f, 0x00, 0x4e, 0xba, 0x0c, 0x98, 0x4f, 0xef, 0x00, 0x0c, 0x52, 0x84, 0xb8, 0xae, 0xff, 0xfa, 0x63, 0x00, 0xfe, 0xac, 0x4a, 0x6e, 0xff, 0xfe, 0x66, 0x06, 0x7a, 0xfe, 0x60, 0x00, 0x00, 0x6e, + 0x4a, 0x46, 0x66, 0x04, 0x42, 0x45, 0x60, 0x64, 0x70, 0x00, 0x30, 0x03, 0x41, 0xed, 0xfc, 0x80, 0x11, 0xbc, 0x00, 0x01, 0x08, 0x00, 0x70, 0x01, 0x12, 0x03, 0xe3, 0xa0, 0x36, 0x00, 0x4a, 0x82, + 0x67, 0x06, 0x30, 0x03, 0x81, 0x6d, 0xfc, 0x8c, 0x4a, 0x6d, 0xfc, 0x88, 0x66, 0x28, 0x3b, 0x7c, 0x00, 0x01, 0xfc, 0x64, 0x41, 0xfa, 0x04, 0x58, 0x2b, 0x48, 0xfc, 0x66, 0x3b, 0x7c, 0x00, 0x65, + 0xfc, 0x6a, 0x42, 0x6d, 0xfc, 0x6c, 0x48, 0x6d, 0xfc, 0x60, 0x4e, 0xba, 0x0c, 0xc0, 0x1b, 0x7c, 0x00, 0x01, 0xfc, 0x7c, 0x58, 0x8f, 0x30, 0x03, 0x81, 0x6d, 0xfc, 0x88, 0x70, 0x00, 0x30, 0x2d, + 0xfc, 0x88, 0x2f, 0x00, 0x4e, 0xba, 0xfa, 0xb4, 0x42, 0x45, 0x58, 0x8f, 0x2f, 0x2e, 0xff, 0xf6, 0x4e, 0xba, 0x0c, 0xe4, 0x30, 0x05, 0x48, 0xc0, 0x58, 0x8f, 0x4c, 0xee, 0x0c, 0x7c, 0xfd, 0xd8, + 0x4e, 0x5e, 0x4e, 0x75, 0x48, 0xe7, 0x38, 0x20, 0x24, 0x6f, 0x00, 0x14, 0x20, 0x2f, 0x00, 0x18, 0x4e, 0xba, 0xfa, 0x80, 0x28, 0x00, 0x34, 0x2a, 0x00, 0x18, 0x46, 0x42, 0x04, 0x42, 0x00, 0x20, + 0x70, 0x01, 0x12, 0x02, 0xe3, 0xa0, 0x46, 0x40, 0xc1, 0x6d, 0xfc, 0x88, 0x70, 0x00, 0x30, 0x2d, 0xfc, 0x88, 0x2f, 0x00, 0x4e, 0xba, 0xfa, 0x64, 0x36, 0x2d, 0xfc, 0x88, 0x48, 0x78, 0x03, 0x08, + 0x32, 0x02, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0x48, 0x70, 0x18, 0x06, 0x4e, 0xba, + 0x0c, 0x04, 0x4a, 0x43, 0x4f, 0xef, 0x00, 0x0c, 0x66, 0x22, 0x4a, 0x2d, 0xfc, 0x7c, 0x67, 0x0a, 0x48, 0x6d, 0xfc, 0x60, 0x4e, 0xba, 0x0c, 0x20, 0x58, 0x8f, 0x4a, 0x2d, 0xfc, 0x78, 0x67, 0x0c, + 0x45, 0xfa, 0x04, 0x40, 0x2f, 0x0a, 0x4e, 0xba, 0xfc, 0x78, 0x58, 0x8f, 0x2f, 0x04, 0x4e, 0xba, 0x0c, 0x46, 0x4a, 0x43, 0x58, 0x8f, 0x66, 0x2a, 0x4e, 0xba, 0x0b, 0x7e, 0x24, 0x00, 0x4e, 0xba, + 0x0b, 0x88, 0x2f, 0x00, 0x4e, 0xba, 0x0b, 0x78, 0x4e, 0xba, 0xf9, 0xe2, 0x2f, 0x00, 0x4e, 0xba, 0x0c, 0x3c, 0x2f, 0x02, 0x4e, 0xba, 0x0b, 0x68, 0x42, 0xa7, 0x4e, 0xba, 0xf9, 0xc6, 0x4f, 0xef, + 0x00, 0x10, 0x70, 0x00, 0x4c, 0xdf, 0x04, 0x1c, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf0, 0x48, 0xe7, 0x3e, 0x38, 0x24, 0x2e, 0x00, 0x08, 0x24, 0x6e, 0x00, 0x0c, 0x4e, 0xba, 0xf9, 0xb6, 0x2d, 0x40, + 0xff, 0xf2, 0x28, 0x42, 0x3c, 0x2c, 0x00, 0x18, 0x46, 0x46, 0x04, 0x46, 0x00, 0x20, 0x32, 0x06, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, + 0x42, 0x40, 0xd2, 0x80, 0x47, 0xed, 0xfc, 0xa0, 0xd7, 0xc1, 0x78, 0x00, 0x0c, 0x2a, 0x00, 0x03, 0x00, 0x07, 0x57, 0xc4, 0x44, 0x04, 0x28, 0x42, 0x2d, 0x6c, 0x00, 0x10, 0xff, 0xfc, 0x70, 0x09, + 0x26, 0x2e, 0xff, 0xfc, 0xe0, 0xa3, 0x2d, 0x43, 0xff, 0xfc, 0x2d, 0x6a, 0x00, 0x24, 0xff, 0xf8, 0x70, 0x09, 0x26, 0x2e, 0xff, 0xf8, 0xe0, 0xa3, 0x2d, 0x43, 0xff, 0xf8, 0x30, 0x2b, 0x00, 0x16, + 0xb0, 0x6a, 0x00, 0x16, 0x67, 0x06, 0x76, 0xc8, 0x60, 0x00, 0x00, 0xe4, 0x20, 0x2a, 0x00, 0x24, 0x02, 0x80, 0x00, 0x00, 0x01, 0xff, 0x66, 0x00, 0x00, 0x18, 0x4a, 0xab, 0x00, 0x1c, 0x67, 0x16, + 0x20, 0x2b, 0x00, 0x20, 0x22, 0x2e, 0xff, 0xf8, 0xd2, 0xae, 0xff, 0xfc, 0xb0, 0x81, 0x64, 0x06, 0x76, 0xce, 0x60, 0x00, 0x00, 0xba, 0x30, 0x06, 0x48, 0xc0, 0x41, 0xed, 0xfc, 0x80, 0x4a, 0x30, + 0x08, 0x00, 0x67, 0x20, 0x30, 0x06, 0x48, 0xc0, 0x41, 0xed, 0xfc, 0x80, 0x42, 0x30, 0x08, 0x00, 0x70, 0x00, 0x10, 0x2d, 0xfc, 0x74, 0x2f, 0x00, 0x30, 0x06, 0x48, 0xc0, 0x2f, 0x00, 0x4e, 0xba, + 0x03, 0xca, 0x50, 0x8f, 0x08, 0x2a, 0x00, 0x06, 0x00, 0x2d, 0x67, 0x0a, 0x4a, 0x44, 0x66, 0x06, 0x42, 0x43, 0x60, 0x00, 0x00, 0x6c, 0x42, 0x6e, 0xff, 0xf6, 0x7a, 0xff, 0x2f, 0x2a, 0x00, 0x20, + 0x20, 0x2b, 0x00, 0x1c, 0xd0, 0xae, 0xff, 0xfc, 0x2f, 0x00, 0x2f, 0x2e, 0xff, 0xf8, 0x30, 0x04, 0x48, 0xc0, 0x2f, 0x00, 0x30, 0x06, 0x48, 0xc0, 0x2f, 0x00, 0x4e, 0xba, 0x04, 0xae, 0x36, 0x00, + 0x4f, 0xef, 0x00, 0x14, 0x67, 0x3a, 0x53, 0x43, 0x66, 0x10, 0x2f, 0x2b, 0x00, 0x28, 0x30, 0x06, 0x48, 0xc0, 0x2f, 0x00, 0x4e, 0xba, 0x07, 0x42, 0x50, 0x8f, 0xba, 0xab, 0x00, 0x28, 0x66, 0x06, + 0x52, 0x6e, 0xff, 0xf6, 0x60, 0x08, 0x42, 0x6e, 0xff, 0xf6, 0x2a, 0x2b, 0x00, 0x28, 0x0c, 0x6e, 0x00, 0x04, 0xff, 0xf6, 0x6d, 0x00, 0xff, 0xa6, 0x76, 0xdc, 0x42, 0xaa, 0x00, 0x28, 0x60, 0x0e, + 0x20, 0x2a, 0x00, 0x24, 0x25, 0x40, 0x00, 0x28, 0x20, 0x42, 0xd1, 0xa8, 0x00, 0x10, 0x2f, 0x2e, 0xff, 0xf2, 0x4e, 0xba, 0x0a, 0xa2, 0x30, 0x03, 0x48, 0xc0, 0x58, 0x8f, 0x4c, 0xee, 0x1c, 0x7c, + 0xff, 0xd0, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xfd, 0xfc, 0x48, 0xe7, 0x3c, 0x30, 0x26, 0x6e, 0x00, 0x08, 0x24, 0x6e, 0x00, 0x0c, 0x4e, 0xba, 0xf8, 0x3a, 0x2a, 0x00, 0x38, 0x2b, 0x00, 0x18, + 0x46, 0x44, 0x04, 0x44, 0x00, 0x20, 0x30, 0x04, 0x48, 0xc0, 0x24, 0x00, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x42, 0xc4, 0xfc, 0x00, 0x6c, 0x48, 0x42, 0x42, 0x42, 0xd0, 0x82, 0x41, 0xed, 0xfc, 0xa0, + 0xd1, 0xc0, 0x42, 0x43, 0x0c, 0x6a, 0x00, 0x41, 0x00, 0x1a, 0x66, 0x44, 0x4a, 0x2d, 0xfc, 0x78, 0x66, 0x34, 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x9f, 0x4e, 0xba, 0x09, 0xda, 0x24, 0x00, + 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x95, 0x4e, 0xba, 0x09, 0xcc, 0xb4, 0x80, 0x4f, 0xef, 0x00, 0x10, 0x67, 0x12, 0x45, 0xfa, 0x02, 0x0c, 0x2f, 0x0a, 0x4e, 0xba, 0xfa, 0x2c, 0x1b, 0x7c, + 0x00, 0x01, 0xfc, 0x78, 0x58, 0x8f, 0x02, 0x6b, 0x4f, 0xff, 0x00, 0x04, 0x60, 0x00, 0x00, 0xb2, 0x30, 0x28, 0x00, 0x16, 0xb0, 0x6a, 0x00, 0x16, 0x67, 0x06, 0x76, 0xc8, 0x60, 0x00, 0x00, 0xa2, + 0x30, 0x2a, 0x00, 0x1a, 0x0c, 0x40, 0x00, 0x07, 0x6d, 0x06, 0x6e, 0x1e, 0x60, 0x00, 0x00, 0x7e, 0x0c, 0x40, 0x00, 0x05, 0x6d, 0x00, 0x00, 0x88, 0x6e, 0x04, 0x60, 0x00, 0x00, 0x84, 0x0c, 0x40, + 0x00, 0x06, 0x66, 0x00, 0x00, 0x7a, 0x60, 0x00, 0x00, 0x78, 0x0c, 0x40, 0x00, 0x11, 0x6d, 0x00, 0x00, 0x6e, 0x6e, 0x02, 0x60, 0x08, 0x0c, 0x40, 0x00, 0x15, 0x66, 0x62, 0x60, 0x18, 0x43, 0xea, + 0x00, 0x1c, 0x0c, 0x51, 0x00, 0x01, 0x66, 0x06, 0x42, 0xa8, 0x00, 0x1c, 0x60, 0x52, 0x21, 0x68, 0x00, 0x18, 0x00, 0x1c, 0x60, 0x4a, 0x4e, 0xba, 0xf7, 0x6c, 0x2d, 0x40, 0xff, 0xfc, 0x20, 0x2e, + 0xff, 0xfc, 0x06, 0x80, 0x00, 0x00, 0x01, 0x00, 0x5c, 0x80, 0x32, 0x04, 0x48, 0xc1, 0x74, 0x30, 0xd2, 0x82, 0x26, 0x40, 0x16, 0x81, 0x48, 0x78, 0x00, 0x04, 0x48, 0x6e, 0xff, 0xfc, 0x48, 0x6a, + 0x00, 0x1c, 0x4e, 0xba, 0x08, 0x02, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x14, 0x70, 0x00, 0x30, 0x28, 0x00, 0x16, 0x2f, 0x00, 0x48, 0x78, 0x00, 0x07, 0x4e, 0xba, 0x08, 0xdc, 0x50, 0x8f, 0x76, 0xef, + 0x2f, 0x05, 0x4e, 0xba, 0x09, 0x52, 0x34, 0x03, 0x48, 0xc2, 0x58, 0x8f, 0x20, 0x02, 0x4c, 0xee, 0x0c, 0x3c, 0xfd, 0xe4, 0x4e, 0x5e, 0x4e, 0x75, 0x48, 0xe7, 0x30, 0x30, 0x26, 0x6f, 0x00, 0x14, + 0x24, 0x6f, 0x00, 0x18, 0x42, 0x42, 0x4e, 0xba, 0xf6, 0xea, 0x26, 0x00, 0x32, 0x2b, 0x00, 0x18, 0x46, 0x41, 0x04, 0x41, 0x00, 0x20, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, + 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0xd1, 0xc1, 0x30, 0x28, 0x00, 0x16, 0xb0, 0x6a, 0x00, 0x16, 0x67, 0x04, 0x74, 0xc8, 0x60, 0x1e, 0x30, 0x2a, + 0x00, 0x1a, 0x51, 0x40, 0x66, 0x14, 0x48, 0x78, 0x00, 0x16, 0x48, 0x50, 0x48, 0x6a, 0x00, 0x1c, 0x4e, 0xba, 0x07, 0x74, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x02, 0x74, 0xee, 0x2f, 0x03, 0x4e, 0xba, + 0x08, 0xd6, 0x30, 0x02, 0x48, 0xc0, 0x58, 0x8f, 0x4c, 0xdf, 0x0c, 0x0c, 0x4e, 0x75, 0x48, 0xe7, 0x38, 0x00, 0x4e, 0xba, 0xf6, 0x7e, 0x28, 0x00, 0x3b, 0x7c, 0x00, 0x65, 0xfc, 0x6a, 0x4a, 0x6d, + 0xfc, 0x8c, 0x67, 0x58, 0x36, 0x3c, 0x00, 0x80, 0x74, 0x07, 0x72, 0x00, 0x32, 0x2d, 0xfc, 0x8c, 0x30, 0x03, 0x48, 0xc0, 0xc2, 0x80, 0x67, 0x38, 0x32, 0x02, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, + 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0x70, 0x00, 0x30, 0x30, 0x18, 0x16, 0x2f, 0x00, 0x48, 0x78, 0x00, 0x07, 0x4e, 0xba, + 0x07, 0xf6, 0x4a, 0x80, 0x50, 0x8f, 0x66, 0x08, 0x30, 0x03, 0x46, 0x40, 0xc1, 0x6d, 0xfc, 0x8c, 0x30, 0x03, 0xe2, 0x40, 0x36, 0x00, 0x53, 0x42, 0x6c, 0x00, 0xff, 0xb0, 0x4a, 0x2d, 0xfc, 0x74, + 0x66, 0x32, 0x22, 0x38, 0x02, 0xae, 0x20, 0x38, 0x0c, 0x54, 0x02, 0x80, 0x00, 0xff, 0xff, 0xff, 0xb2, 0x80, 0x63, 0x20, 0x1b, 0x7c, 0x00, 0x01, 0xfc, 0x74, 0x42, 0x42, 0x30, 0x02, 0x48, 0xc0, + 0x41, 0xed, 0xfc, 0x80, 0x11, 0xbc, 0x00, 0x01, 0x08, 0x00, 0x52, 0x42, 0x0c, 0x42, 0x00, 0x08, 0x6d, 0x00, 0xff, 0xea, 0x2f, 0x04, 0x4e, 0xba, 0x08, 0x1e, 0x58, 0x8f, 0x4c, 0xdf, 0x00, 0x1c, + 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf8, 0x48, 0xe7, 0x3c, 0x00, 0x4e, 0xba, 0xf5, 0xc6, 0x2a, 0x00, 0x48, 0x78, 0x00, 0x06, 0x48, 0x78, 0x00, 0x1b, 0x48, 0x6e, 0xff, 0xfa, 0x4e, 0xba, 0x06, 0xa2, + 0x70, 0x01, 0x12, 0x2d, 0xfc, 0x70, 0xe3, 0xa0, 0x38, 0x00, 0x74, 0x00, 0x4f, 0xef, 0x00, 0x0c, 0x20, 0x02, 0xd0, 0x80, 0x41, 0xed, 0xfc, 0x90, 0x36, 0x30, 0x08, 0x00, 0x70, 0x00, 0x30, 0x04, + 0x72, 0x00, 0x32, 0x03, 0xc0, 0x81, 0x67, 0x46, 0x4a, 0x82, 0x67, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x30, 0x03, 0x02, 0x80, 0x00, 0x00, 0x40, 0x00, 0x66, 0x34, 0x70, 0x00, 0x30, 0x03, 0x02, 0x80, + 0x00, 0x00, 0x80, 0x00, 0x67, 0x08, 0x1d, 0x7c, 0x00, 0x01, 0xff, 0xfb, 0x60, 0x04, 0x42, 0x2e, 0xff, 0xfb, 0x48, 0x78, 0x03, 0x84, 0x42, 0xa7, 0x42, 0xa7, 0x42, 0xa7, 0x2f, 0x02, 0x48, 0x78, + 0x00, 0x06, 0x48, 0x6e, 0xff, 0xfa, 0x4e, 0xba, 0x02, 0x70, 0x4f, 0xef, 0x00, 0x1c, 0x52, 0x82, 0x70, 0x08, 0xb0, 0x82, 0x6e, 0x00, 0xff, 0x9a, 0x2f, 0x05, 0x4e, 0xba, 0x07, 0x7a, 0x58, 0x8f, + 0x4c, 0xee, 0x00, 0x3c, 0xff, 0xe8, 0x4e, 0x5e, 0x4e, 0x75, 0x48, 0xe7, 0x30, 0x00, 0x22, 0x2f, 0x00, 0x0c, 0x26, 0x2f, 0x00, 0x10, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, + 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0xd1, 0xc1, 0x22, 0x48, 0x70, 0x01, 0xb0, 0x83, 0x66, 0x1a, 0x70, 0x00, 0x10, 0x29, 0x00, 0x27, 0x72, 0x01, 0x14, 0x2d, + 0xfc, 0x70, 0xe5, 0xa1, 0xc0, 0x81, 0x67, 0x08, 0x13, 0x7c, 0x00, 0x01, 0x00, 0x26, 0x60, 0x04, 0x42, 0x29, 0x00, 0x26, 0x33, 0x7c, 0x00, 0x01, 0x00, 0x30, 0x4a, 0x83, 0x67, 0x00, 0x00, 0x08, + 0x4a, 0x29, 0x00, 0x25, 0x66, 0x16, 0x42, 0x29, 0x00, 0x24, 0x23, 0x7c, 0x00, 0x00, 0x02, 0x00, 0x00, 0x36, 0x33, 0x7c, 0x00, 0x07, 0x00, 0x3a, 0x60, 0x00, 0x00, 0xaa, 0x13, 0x69, 0x00, 0x25, + 0x00, 0x24, 0x70, 0x00, 0x10, 0x29, 0x00, 0x24, 0x0c, 0x40, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x96, 0x6e, 0x02, 0x60, 0x08, 0x55, 0x40, 0x66, 0x00, 0x00, 0x8c, 0x60, 0x22, 0x23, 0x7c, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x36, 0x33, 0x7c, 0x00, 0x05, 0x00, 0x3a, 0x70, 0xf6, 0x23, 0x40, 0x00, 0x3c, 0x33, 0x7c, 0x00, 0x07, 0x00, 0x44, 0x33, 0x7c, 0x00, 0x01, 0x00, 0x2e, 0x60, 0x66, 0x70, 0x00, + 0x30, 0x29, 0x00, 0x2c, 0x23, 0x40, 0x00, 0x36, 0x33, 0x7c, 0x00, 0x04, 0x00, 0x3a, 0x41, 0xe9, 0x00, 0x32, 0x23, 0x48, 0x00, 0x3c, 0x41, 0xe9, 0x00, 0x46, 0x23, 0x48, 0x00, 0x40, 0x33, 0x7c, + 0x00, 0x01, 0x00, 0x44, 0x20, 0x3c, 0x00, 0x00, 0x02, 0x00, 0x72, 0x00, 0x32, 0x29, 0x00, 0x2c, 0x90, 0x81, 0x23, 0x40, 0x00, 0x4a, 0x33, 0x7c, 0x00, 0x04, 0x00, 0x4e, 0x41, 0xe9, 0x00, 0x46, + 0x23, 0x48, 0x00, 0x50, 0x41, 0xe9, 0x00, 0x32, 0x23, 0x48, 0x00, 0x54, 0x33, 0x7c, 0x00, 0x05, 0x00, 0x58, 0x70, 0xd8, 0x23, 0x40, 0x00, 0x5a, 0x33, 0x7c, 0x00, 0x07, 0x00, 0x62, 0x33, 0x7c, + 0x00, 0x04, 0x00, 0x2e, 0x4c, 0xdf, 0x00, 0x0c, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf0, 0x48, 0xe7, 0x3c, 0x20, 0x38, 0x2e, 0x00, 0x0a, 0x36, 0x2e, 0x00, 0x0e, 0x24, 0x2e, 0x00, 0x10, 0x4a, 0x43, + 0x67, 0x04, 0x70, 0x0a, 0x60, 0x02, 0x70, 0x08, 0x48, 0x78, 0x00, 0x06, 0x2f, 0x00, 0x48, 0x6e, 0xff, 0xf2, 0x4e, 0xba, 0x04, 0xcc, 0x72, 0x00, 0x32, 0x04, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, + 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x45, 0xed, 0xfc, 0xa0, 0xd5, 0xc1, 0x25, 0x6e, 0x00, 0x18, 0x00, 0x32, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x00, 0x00, 0xc8, + 0x20, 0x2e, 0x00, 0x14, 0x72, 0x10, 0xe2, 0xa8, 0x1d, 0x40, 0xff, 0xf3, 0x41, 0xee, 0xff, 0xf4, 0x30, 0xae, 0x00, 0x16, 0x4a, 0x2a, 0x00, 0x24, 0x66, 0x04, 0x7a, 0x01, 0x60, 0x28, 0x0c, 0x82, + 0x00, 0x00, 0x01, 0x00, 0x63, 0x08, 0x2a, 0x3c, 0x00, 0x00, 0x01, 0x00, 0x60, 0x02, 0x2a, 0x02, 0x72, 0x00, 0x32, 0x2a, 0x00, 0x2e, 0xd2, 0x81, 0x20, 0x01, 0xe5, 0x81, 0xd2, 0x80, 0x20, 0x05, + 0x2a, 0x00, 0x25, 0x80, 0x18, 0x36, 0x1d, 0x45, 0xff, 0xf6, 0x48, 0x78, 0x2a, 0x30, 0x70, 0x00, 0x10, 0x2a, 0x00, 0x26, 0x2f, 0x00, 0x70, 0x00, 0x30, 0x03, 0x2f, 0x00, 0x48, 0x6a, 0x00, 0x30, + 0x70, 0x00, 0x30, 0x04, 0x2f, 0x00, 0x48, 0x78, 0x00, 0x06, 0x48, 0x6e, 0xff, 0xf2, 0x4e, 0xba, 0x00, 0x68, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x1c, 0x67, 0x44, 0x0c, 0x40, 0x00, 0x02, 0x67, 0x04, + 0x48, 0xc0, 0x60, 0x4a, 0x48, 0x78, 0x00, 0x08, 0x48, 0x6e, 0xff, 0xf8, 0x70, 0x00, 0x30, 0x04, 0x2f, 0x00, 0x4e, 0xba, 0x01, 0x58, 0x4a, 0x80, 0x4f, 0xef, 0x00, 0x0c, 0x67, 0x04, 0x70, 0xf9, + 0x60, 0x2c, 0x48, 0x78, 0x00, 0x04, 0x48, 0x6e, 0xff, 0xfb, 0x48, 0x6a, 0x00, 0x28, 0x4e, 0xba, 0x03, 0xd6, 0x70, 0x00, 0x10, 0x2e, 0xff, 0xfa, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x10, 0x94, 0x85, + 0x20, 0x05, 0xd1, 0xae, 0x00, 0x14, 0x4a, 0x82, 0x62, 0x00, 0xff, 0x36, 0x70, 0x00, 0x4c, 0xee, 0x04, 0x3c, 0xff, 0xdc, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf8, 0x48, 0xe7, 0x3e, 0x00, + 0x26, 0x2e, 0x00, 0x08, 0x38, 0x2e, 0x00, 0x0e, 0x3a, 0x2e, 0x00, 0x12, 0x42, 0x46, 0x42, 0x6e, 0xff, 0xfe, 0x55, 0x8f, 0x4e, 0xba, 0x03, 0xcc, 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x1e, + 0x52, 0x6e, 0xff, 0xfe, 0x0c, 0x6e, 0x00, 0x03, 0xff, 0xfe, 0x6f, 0x00, 0xff, 0xe6, 0x55, 0x8f, 0x4e, 0xba, 0x04, 0x00, 0x30, 0x1f, 0x08, 0x00, 0x00, 0x06, 0x67, 0xd2, 0x60, 0xf0, 0x55, 0x8f, + 0x3f, 0x05, 0x4e, 0xba, 0x03, 0xa8, 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x06, 0x70, 0xfe, 0x60, 0x00, 0x00, 0xb0, 0x42, 0x6e, 0xff, 0xfe, 0x55, 0x8f, 0x2f, 0x03, 0x3f, 0x04, 0x4e, 0xba, + 0x03, 0x96, 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x12, 0x52, 0x6e, 0xff, 0xfe, 0x0c, 0x6e, 0x00, 0x03, 0xff, 0xfe, 0x6f, 0x00, 0xff, 0xe2, 0x7c, 0xfd, 0x60, 0x52, 0x4a, 0xae, 0x00, 0x14, + 0x67, 0x4c, 0x4a, 0x6e, 0x00, 0x1e, 0x67, 0x24, 0x4a, 0x6e, 0x00, 0x1a, 0x67, 0x0c, 0x55, 0x8f, 0x2f, 0x2e, 0x00, 0x14, 0x4e, 0xba, 0x03, 0x92, 0x60, 0x0a, 0x55, 0x8f, 0x2f, 0x2e, 0x00, 0x14, + 0x4e, 0xba, 0x03, 0x7c, 0x30, 0x1f, 0x48, 0xc0, 0x2c, 0x00, 0x60, 0x22, 0x4a, 0x6e, 0x00, 0x1a, 0x67, 0x0c, 0x55, 0x8f, 0x2f, 0x2e, 0x00, 0x14, 0x4e, 0xba, 0x03, 0x5a, 0x60, 0x0a, 0x55, 0x8f, + 0x2f, 0x2e, 0x00, 0x14, 0x4e, 0xba, 0x03, 0x44, 0x30, 0x1f, 0x48, 0xc0, 0x2c, 0x00, 0x55, 0x8f, 0x48, 0x6e, 0xff, 0xfa, 0x48, 0x6e, 0xff, 0xfc, 0x2f, 0x2e, 0x00, 0x20, 0x4e, 0xba, 0x03, 0x22, + 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x04, 0x70, 0xfc, 0x60, 0x16, 0x4a, 0x46, 0x67, 0x0c, 0x0c, 0x6e, 0x00, 0x02, 0xff, 0xfa, 0x67, 0x04, 0x30, 0x06, 0x60, 0x04, 0x30, 0x2e, 0xff, 0xfa, + 0x48, 0xc0, 0x4c, 0xee, 0x00, 0x7c, 0xff, 0xe4, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xe4, 0x48, 0xe7, 0x38, 0x00, 0x36, 0x2e, 0x00, 0x0a, 0x28, 0x2e, 0x00, 0x0c, 0x34, 0x2e, 0x00, 0x12, + 0x48, 0x78, 0x00, 0x06, 0x48, 0x78, 0x00, 0x03, 0x48, 0x6e, 0xff, 0xe6, 0x4e, 0xba, 0x02, 0x92, 0x1d, 0x42, 0xff, 0xea, 0x3d, 0x7c, 0x00, 0x02, 0xff, 0xec, 0x2d, 0x44, 0xff, 0xee, 0x70, 0x00, + 0x30, 0x02, 0x2d, 0x40, 0xff, 0xf2, 0x3d, 0x7c, 0x00, 0x07, 0xff, 0xf6, 0x48, 0x78, 0x02, 0x58, 0x42, 0xa7, 0x42, 0xa7, 0x48, 0x6e, 0xff, 0xec, 0x30, 0x03, 0x48, 0xc0, 0x2f, 0x00, 0x48, 0x78, + 0x00, 0x06, 0x48, 0x6e, 0xff, 0xe6, 0x4e, 0xba, 0xfe, 0x90, 0x4f, 0xef, 0x00, 0x28, 0x4c, 0xee, 0x00, 0x1c, 0xff, 0xd8, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf4, 0x48, 0xe7, 0x3e, 0x30, + 0x24, 0x2e, 0x00, 0x08, 0x26, 0x2e, 0x00, 0x0c, 0x4e, 0xba, 0x02, 0xce, 0x2d, 0x40, 0xff, 0xf6, 0x4e, 0xba, 0x02, 0xd6, 0x2f, 0x00, 0x4e, 0xba, 0x02, 0xc6, 0x48, 0x78, 0x04, 0x00, 0x4e, 0xba, + 0x02, 0xd8, 0x24, 0x40, 0xb4, 0xfc, 0x00, 0x00, 0x50, 0x8f, 0x66, 0x1e, 0x4e, 0xba, 0x02, 0xc2, 0x2f, 0x00, 0x4e, 0xba, 0x02, 0xaa, 0x48, 0x78, 0x04, 0x00, 0x4e, 0xba, 0x02, 0xbc, 0x24, 0x40, + 0xb4, 0xfc, 0x00, 0x00, 0x50, 0x8f, 0x67, 0x00, 0x01, 0x16, 0x2f, 0x0a, 0x4e, 0xba, 0x02, 0xbe, 0x26, 0x52, 0x20, 0x4b, 0x41, 0xe8, 0x02, 0x00, 0x2d, 0x48, 0xff, 0xfc, 0x42, 0x6e, 0xff, 0xfa, + 0x58, 0x8f, 0x42, 0x45, 0x42, 0x46, 0x42, 0x44, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0xd2, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, + 0x67, 0x20, 0x53, 0x40, 0x66, 0x18, 0x52, 0x46, 0xba, 0x44, 0x66, 0x12, 0x48, 0x78, 0x02, 0x00, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x0b, 0x4e, 0xba, 0x01, 0x8e, 0x4f, 0xef, 0x00, 0x0c, 0x52, 0x45, + 0x60, 0x12, 0x48, 0x78, 0x02, 0x00, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x0b, 0x4e, 0xba, 0x01, 0x78, 0x4f, 0xef, 0x00, 0x0c, 0x52, 0x44, 0x0c, 0x44, 0x00, 0x0a, 0x6d, 0x00, 0xff, 0xac, 0x4a, 0x45, + 0x67, 0x00, 0x00, 0x9c, 0x4a, 0x46, 0x67, 0x00, 0x00, 0x96, 0x42, 0x44, 0x2f, 0x0b, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x01, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0x6e, 0x30, 0x00, + 0x4f, 0xef, 0x00, 0x14, 0x66, 0x00, 0x00, 0x28, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0x52, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, + 0x66, 0x00, 0x00, 0x0c, 0x52, 0x44, 0x0c, 0x44, 0x00, 0x0a, 0x6c, 0x52, 0x60, 0xbe, 0x52, 0x6e, 0xff, 0xfa, 0x0c, 0x6e, 0x00, 0x02, 0xff, 0xfa, 0x6e, 0x00, 0x00, 0x44, 0x2f, 0x03, 0x2f, 0x02, + 0x4e, 0xba, 0x00, 0x56, 0x50, 0x8f, 0x2f, 0x0b, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x01, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0x14, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, 0x66, 0x00, + 0x00, 0x1e, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x2f, 0x02, 0x4e, 0xba, 0xfb, 0xf8, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, 0x66, 0x00, 0xff, 0x06, 0x2f, 0x0a, + 0x4e, 0xba, 0x01, 0xa0, 0x2f, 0x2e, 0xff, 0xf6, 0x4e, 0xba, 0x01, 0x74, 0x50, 0x8f, 0x4c, 0xee, 0x0c, 0x7c, 0xff, 0xd8, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xd4, 0x48, 0xe7, 0x30, 0x00, + 0x34, 0x2e, 0x00, 0x0a, 0x26, 0x2e, 0x00, 0x0c, 0x48, 0x78, 0x00, 0x06, 0x48, 0x78, 0x00, 0x07, 0x48, 0x6e, 0xff, 0xde, 0x4e, 0xba, 0x00, 0xaa, 0x48, 0x78, 0x00, 0x08, 0x42, 0xa7, 0x48, 0x6e, + 0xff, 0xd6, 0x4e, 0xba, 0x00, 0x9c, 0x1d, 0x7c, 0x00, 0x04, 0xff, 0xd9, 0x3d, 0x7c, 0x00, 0x02, 0xff, 0xec, 0x41, 0xee, 0xff, 0xd6, 0x2d, 0x48, 0xff, 0xee, 0x70, 0x08, 0x2d, 0x40, 0xff, 0xf2, + 0x3d, 0x7c, 0x00, 0x07, 0xff, 0xf6, 0x41, 0xee, 0xff, 0xda, 0x20, 0x83, 0x48, 0x78, 0x0e, 0x10, 0x42, 0xa7, 0x48, 0x78, 0x00, 0x01, 0x48, 0x6e, 0xff, 0xec, 0x30, 0x02, 0x48, 0xc0, 0x2f, 0x00, + 0x48, 0x78, 0x00, 0x06, 0x48, 0x6e, 0xff, 0xde, 0x4e, 0xba, 0xfc, 0x8e, 0x4a, 0x80, 0x4f, 0xef, 0x00, 0x34, 0x67, 0x26, 0x48, 0x78, 0x00, 0x08, 0x48, 0x6e, 0xff, 0xe4, 0x30, 0x02, 0x48, 0xc0, + 0x2f, 0x00, 0x4e, 0xba, 0xfd, 0x88, 0x4a, 0x80, 0x4f, 0xef, 0x00, 0x0c, 0x67, 0x04, 0x70, 0xff, 0x60, 0x0a, 0x70, 0x00, 0x10, 0x2e, 0xff, 0xe6, 0x60, 0x02, 0x70, 0x00, 0x4c, 0xee, 0x00, 0x0c, + 0xff, 0xcc, 0x4e, 0x5e, 0x4e, 0x75, 0x20, 0x6f, 0x00, 0x04, 0x22, 0x6f, 0x00, 0x08, 0x20, 0x2f, 0x00, 0x0c, 0x60, 0x04, 0x10, 0xd9, 0x53, 0x80, 0x4a, 0x80, 0x66, 0x00, 0xff, 0xf8, 0x4e, 0x75, + 0x22, 0x6f, 0x00, 0x04, 0x10, 0x2f, 0x00, 0x0b, 0x32, 0x2f, 0x00, 0x0e, 0x48, 0xc1, 0x60, 0x06, 0x20, 0x41, 0xd1, 0xc9, 0x42, 0x10, 0x53, 0x81, 0x4a, 0x81, 0x6c, 0x00, 0xff, 0xf4, 0x12, 0x80, + 0x4e, 0x75, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x01, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x02, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x03, 0x2f, 0x08, 0xac, 0x15, + 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x04, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x05, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x06, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, + 0x3f, 0x3c, 0x00, 0x08, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x09, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x0a, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x6f, 0x00, 0x0c, + 0x30, 0x2f, 0x00, 0x0a, 0x48, 0x40, 0x30, 0x2f, 0x00, 0x06, 0xa0, 0x4e, 0x4e, 0x75, 0x70, 0x00, 0x31, 0xc0, 0x02, 0x20, 0x20, 0x08, 0x4e, 0x75, 0xa1, 0x1a, 0x4e, 0xfa, 0xff, 0xf4, 0x20, 0x6f, + 0x00, 0x04, 0xa0, 0x1b, 0x4e, 0xfa, 0xff, 0xea, 0x20, 0x78, 0x02, 0xa6, 0x4e, 0xfa, 0xff, 0xe0, 0x20, 0x78, 0x02, 0xaa, 0x4e, 0xfa, 0xff, 0xd8, 0x20, 0x2f, 0x00, 0x04, 0xa1, 0x22, 0x4e, 0xfa, + 0xff, 0xd0, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x23, 0x4e, 0xfa, 0xff, 0xc6, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x29, 0x4e, 0xfa, 0xff, 0xbc, 0x30, 0x6f, 0x00, 0x06, 0x20, 0x2f, 0x00, 0x08, 0xa0, 0x2f, + 0x48, 0xc0, 0x4e, 0x75, 0x22, 0x6f, 0x00, 0x08, 0x20, 0x6f, 0x00, 0x04, 0x2f, 0x02, 0xa9, 0x6e, 0x24, 0x1f, 0x48, 0xc0, 0x4e, 0x75, 0x30, 0x2f, 0x00, 0x06, 0x12, 0x2f, 0x00, 0x0b, 0x4a, 0x01, + 0x67, 0x04, 0xa7, 0x46, 0x60, 0x02, 0xa3, 0x46, 0x20, 0x08, 0x4e, 0x75, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x33, 0x48, 0xc0, 0x4e, 0x75, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x34, 0x48, 0xc0, 0x4e, 0x75, + 0x4e, 0xba, 0x00, 0x58, 0x06, 0x80, 0x00, 0x00, 0x00, 0x20, 0x4e, 0x75, 0x2f, 0x0d, 0x20, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x66, 0x0c, 0x20, 0x6f, 0x00, 0x08, 0x70, 0x07, 0x20, 0xdd, 0x51, 0xc8, + 0xff, 0xfc, 0x2a, 0x6f, 0x00, 0x08, 0x4e, 0xba, 0x00, 0x3a, 0x2a, 0x5f, 0x4e, 0x75, 0x20, 0x0d, 0x2a, 0x6f, 0x00, 0x04, 0x4e, 0x75, 0x2a, 0x6f, 0x00, 0x04, 0x4e, 0x75, 0x4e, 0xba, 0xff, 0xc2, + 0xa1, 0x1e, 0x2f, 0x08, 0x4e, 0xba, 0x00, 0x14, 0xd0, 0x9f, 0x4e, 0x75, 0x4e, 0xba, 0x00, 0x0c, 0x20, 0x6f, 0x00, 0x04, 0x91, 0xc0, 0xa0, 0x1f, 0x4e, 0x75, 0x41, 0xfa, 0x00, 0xce, 0x20, 0x10, + 0x4e, 0x75, 0x48, 0xe7, 0xff, 0xf0, 0x42, 0x47, 0x41, 0xfa, 0x00, 0xc0, 0x22, 0x18, 0x6f, 0x00, 0x00, 0xa6, 0x2a, 0x18, 0x66, 0x04, 0x2a, 0x0d, 0x9a, 0x81, 0x26, 0x45, 0x24, 0x4b, 0xe2, 0x81, + 0x60, 0x02, 0x42, 0x5a, 0x51, 0xc9, 0xff, 0xfc, 0x30, 0x18, 0x3e, 0x18, 0x60, 0x00, 0x00, 0x84, 0x78, 0x00, 0x18, 0x18, 0x22, 0x04, 0x02, 0x01, 0x00, 0x0f, 0x08, 0x04, 0x00, 0x04, 0x67, 0x0e, + 0xe1, 0x41, 0x12, 0x18, 0x08, 0x81, 0x00, 0x0b, 0x67, 0x04, 0xe1, 0x81, 0x12, 0x18, 0x74, 0x01, 0x08, 0x04, 0x00, 0x07, 0x67, 0x16, 0x14, 0x18, 0x08, 0x82, 0x00, 0x07, 0x67, 0x0e, 0xe1, 0x42, + 0x14, 0x18, 0x08, 0x82, 0x00, 0x0e, 0x67, 0x04, 0xe1, 0x82, 0x14, 0x18, 0x7c, 0x02, 0x4e, 0xba, 0x00, 0x4c, 0x08, 0x85, 0x00, 0x0f, 0x67, 0x04, 0xe1, 0x85, 0x1a, 0x18, 0x43, 0xf3, 0x58, 0x00, + 0x24, 0x49, 0x08, 0x04, 0x00, 0x05, 0x67, 0x1c, 0x2c, 0x01, 0x4e, 0xba, 0x00, 0x30, 0x0c, 0x41, 0x00, 0x02, 0x67, 0x06, 0x6d, 0x08, 0xdb, 0x92, 0x60, 0x0e, 0xdb, 0x52, 0x60, 0x0a, 0xdb, 0x12, + 0x60, 0x06, 0x12, 0xd8, 0x51, 0xc9, 0xff, 0xfc, 0x08, 0x04, 0x00, 0x06, 0x67, 0x04, 0x26, 0x0d, 0xd7, 0x92, 0x51, 0xc8, 0xff, 0x7c, 0x4c, 0xdf, 0x0f, 0xff, 0x4e, 0x75, 0x7a, 0x00, 0x60, 0x04, + 0xe1, 0x8d, 0x1a, 0x18, 0x51, 0xce, 0xff, 0xfa, 0x4e, 0x75, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x8a, 0x25, 0x41, 0x35, 0x69, 0x80, 0x00, + 0x03, 0x7e, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x19, 0x1a, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x43, 0x53, 0x49, 0x20, 0x64, 0x72, 0x69, 0x76, + 0x65, 0x73, 0x2e, 0x2c, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x2e, 0x2e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, + }; + + return driver; } -std::vector Mapper::convert_source_block(ssize_t source_address, std::vector source_data) { - // Addresses greater than or equal to zero map to the actual disk image. - if(source_address >= 0) return source_data; - - // Switch to mapping relative to 0, for personal sanity. - source_address += 0x60; - - // Block 0 is the device descriptor, which lists the total number of blocks, - // and provides an offset to the driver. - if(!source_address) { - uint32_t total_device_blocks = uint32_t(number_of_blocks_ + 0x60); - - /* The driver descriptor. */ - std::vector driver_description = { - 0x45, 0x52, /* device signature */ - 0x02, 0x00, /* block size, in bytes */ - - uint8_t(total_device_blocks >> 24), - uint8_t(total_device_blocks >> 16), - uint8_t(total_device_blocks >> 8), - uint8_t(total_device_blocks), - /* number of blocks on device */ - - 0x00, 0x01, /* reserved (formerly: device type) */ - 0x00, 0x01, /* reserved (formerly: device ID) */ - 0x00, 0x00, - 0x00, 0x00, /* reserved ('sbData', no further explanation given) */ - - 0x00, 0x01, /* number of device descriptor entries */ - 0x00, 0x00, - 0x00, 0x40, /* first device descriptor's starting block */ - 0x00, 0x0a, /* size of device driver */ - 0x00, 0x01, /* - More modern documentation: operating system (MacOS = 1) - Inside Macintosh IV: system type (Mac Plus = 1) - */ - }; - driver_description.resize(512); - - return driver_description; - } - - // Blocks 1, 2 and 3 contain parts of the partition map. - if(source_address < 4) { - struct Partition { - const char *name, *type; - uint32_t start_block, size; - uint8_t status; - } partitions[3] = { - { "MacOS", "Apple_HFS", 0x60, uint32_t(number_of_blocks_), 0xb7 }, - { "Apple", "Apple_partition_map", 0x01, 0x3f, 0x37 }, - { "Macintosh", "Apple_Driver", 0x40, 0x20, 0x7f }, - }; - - std::vector partition(512); - - // Fill in the fixed fields. - partition[0] = 'P'; partition[1] = 'M'; /* Signature. */ - partition[7] = 3; /* Number of partitions. */ - - const Partition &details = partitions[source_address-1]; - - partition[8] = uint8_t(details.start_block >> 24); - partition[9] = uint8_t(details.start_block >> 16); - partition[10] = uint8_t(details.start_block >> 8); - partition[11] = uint8_t(details.start_block); - - partition[84] = partition[12] = uint8_t(details.size >> 24); - partition[85] = partition[13] = uint8_t(details.size >> 16); - partition[86] = partition[14] = uint8_t(details.size >> 8); - partition[87] = partition[15] = uint8_t(details.size); - - // 32 bytes are allocated for each of the following strings. - memcpy(&partition[16], details.name, strlen(details.name)); - memcpy(&partition[48], details.type, strlen(details.type)); - - partition[91] = details.status; - - // The third entry in this constructed partition map is the driver; - // add some additional details. - if(source_address == 3) { - partition[98] = 0x13; - partition[99] = 0x9e; /* This version of the driver code is 0x139e bytes long. */ - - partition[118] = 0x84; - partition[119] = 0xb9; /* Driver checksum. */ - - memcpy(&partition[120], "68000", strlen("68000")); /* Driver is for the 68000. */ - - // Various non-zero values that Apple HD SC Tool wrote are below; they are - // documented as reserved officially, so I don't know their meaning. - partition[137] = 0x01; - partition[138] = 0x06; - partition[143] = 0x01; - partition[147] = 0x02; - partition[149] = 0x07; - } - - return partition; - } - - // The ten blocks starting at 0x40 contain the SCSI driver. - if(source_address >= 0x40 && source_address < 0x40 + 10) { - // This is the body of the SCSI driver. - const uint8_t driver[] = { - 0x60, 0x00, 0x01, 0xba, 0x23, 0x24, 0x00, 0x01, 0x00, 0x27, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x13, 0x9e, 0x00, 0x00, 0xda, 0xe7, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, - 0x00, 0x7c, 0x00, 0x68, 0x00, 0x72, 0x00, 0x5e, 0x07, 0x2e, 0x53, 0x43, 0x53, 0x49, 0x30, 0x30, 0x41, 0xbd, 0x30, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x1f, 0x60, 0x00, - 0x02, 0x12, 0x41, 0xfa, 0xff, 0xf4, 0x20, 0xaf, 0x00, 0x04, 0x4e, 0x75, 0x20, 0x3a, 0xff, 0xea, 0x4e, 0x75, 0x20, 0x0d, 0x2a, 0x7a, 0xff, 0xe2, 0x4e, 0x75, 0x41, 0xfa, 0xff, 0xd8, 0x30, 0xaf, - 0x00, 0x06, 0x4e, 0x75, 0x48, 0x7a, 0x00, 0x4e, 0x20, 0x1f, 0x4e, 0x75, 0x70, 0x00, 0x31, 0x40, 0x00, 0x10, 0x60, 0x2a, 0x2f, 0x08, 0x2f, 0x09, 0x4e, 0xba, 0x05, 0x4a, 0x60, 0x1c, 0x2f, 0x08, - 0x2f, 0x09, 0x4e, 0xba, 0x07, 0x82, 0x60, 0x12, 0x2f, 0x08, 0x2f, 0x09, 0x4e, 0xba, 0x08, 0xca, 0x60, 0x08, 0x2f, 0x08, 0x2f, 0x09, 0x4e, 0xba, 0x05, 0xf2, 0x22, 0x5f, 0x20, 0x5f, 0x4a, 0x40, - 0x67, 0x04, 0x31, 0xc0, 0x01, 0x42, 0x08, 0x28, 0x00, 0x09, 0x00, 0x06, 0x66, 0x04, 0x2f, 0x38, 0x08, 0xfc, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, - 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, - 0x80, 0x00, 0x00, 0x01, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x53, 0x43, 0x53, 0x49, 0x20, 0x30, 0x00, 0x48, 0xe7, 0x3f, 0x3e, - 0x49, 0xfa, 0xfe, 0x50, 0x26, 0x14, 0x42, 0x94, 0x70, 0x00, 0x42, 0x42, 0x28, 0x3a, 0xfe, 0x40, 0x53, 0x44, 0x49, 0xfa, 0xfe, 0x2c, 0x14, 0x1c, 0xd0, 0x42, 0xe3, 0x58, 0x51, 0xcc, 0xff, 0xf8, - 0x4a, 0x40, 0x66, 0x02, 0x53, 0x40, 0x49, 0xfa, 0xfe, 0x2a, 0x28, 0x80, 0xb6, 0x80, 0x67, 0x0e, 0x49, 0xfa, 0xfe, 0x20, 0x38, 0x83, 0x4c, 0xdf, 0x7c, 0xfc, 0x70, 0xff, 0x4e, 0x75, 0x28, 0x48, - 0x41, 0xfa, 0xfd, 0xfe, 0x20, 0x3a, 0xfe, 0x08, 0xa0, 0x20, 0x70, 0x06, 0x22, 0x78, 0x01, 0x1c, 0xd2, 0xfc, 0x00, 0x9c, 0x24, 0x21, 0x67, 0x1e, 0x24, 0x42, 0x20, 0x52, 0x24, 0x50, 0x0c, 0xaa, - 0x41, 0xbd, 0x30, 0xb0, 0x00, 0x1a, 0x66, 0x0e, 0xb6, 0xaa, 0xff, 0xfc, 0x66, 0x08, 0x41, 0xfa, 0xfd, 0xd0, 0x4e, 0xea, 0x00, 0x26, 0x51, 0xc8, 0xff, 0xdc, 0x4a, 0x85, 0x6b, 0x14, 0x20, 0x78, - 0x02, 0xa6, 0x20, 0x50, 0xd0, 0xfc, 0x1f, 0x40, 0x2f, 0x38, 0x01, 0x18, 0xa0, 0x57, 0x21, 0xdf, 0x01, 0x18, 0x70, 0x20, 0xd0, 0x45, 0x38, 0x00, 0x46, 0x40, 0xa0, 0x3d, 0xe5, 0x44, 0x22, 0x78, - 0x01, 0x1c, 0x20, 0x71, 0x40, 0x00, 0x2c, 0x50, 0xa0, 0x29, 0x41, 0xfa, 0xfd, 0xaa, 0x2c, 0x88, 0x3d, 0x58, 0x00, 0x04, 0x2d, 0x58, 0x00, 0x22, 0x3d, 0x58, 0x00, 0x26, 0x08, 0xee, 0x00, 0x05, - 0x00, 0x05, 0x08, 0xae, 0x00, 0x06, 0x00, 0x05, 0x70, 0x01, 0x08, 0x05, 0x00, 0x1e, 0x67, 0x02, 0x70, 0x00, 0x2f, 0x00, 0x2f, 0x0e, 0x4e, 0xba, 0x00, 0x3c, 0x50, 0x8f, 0x4a, 0x40, 0x67, 0x02, - 0x70, 0xe9, 0x4c, 0xdf, 0x7c, 0xfc, 0x4e, 0x75, 0x2f, 0x02, 0x42, 0x67, 0x2f, 0x2f, 0x00, 0x0a, 0x3f, 0x3c, 0x00, 0x01, 0x3f, 0x3c, 0x00, 0x03, 0xa8, 0x95, 0x54, 0x8f, 0x24, 0x1f, 0x4e, 0x75, - 0x2f, 0x02, 0x42, 0x67, 0x2f, 0x2f, 0x00, 0x0a, 0x3f, 0x3c, 0x00, 0x04, 0xa8, 0x95, 0x54, 0x8f, 0x24, 0x1f, 0x4e, 0x75, 0x4e, 0x56, 0xfd, 0xf4, 0x48, 0xe7, 0x3e, 0x30, 0x26, 0x6e, 0x00, 0x08, - 0x24, 0x2e, 0x00, 0x0c, 0x4e, 0xba, 0xfd, 0x66, 0x4a, 0x80, 0x67, 0x0c, 0x4e, 0xba, 0xfd, 0x64, 0x2d, 0x40, 0xff, 0xf6, 0x60, 0x00, 0x00, 0x9a, 0x4e, 0xba, 0x0e, 0xde, 0x28, 0x00, 0x4e, 0xba, - 0x0e, 0xe8, 0x2f, 0x00, 0x4e, 0xba, 0x0e, 0xd8, 0x4e, 0xba, 0x0f, 0x92, 0x26, 0x00, 0x2f, 0x04, 0x4e, 0xba, 0x0e, 0xcc, 0x4a, 0x83, 0x50, 0x8f, 0x66, 0x06, 0x70, 0xff, 0x60, 0x00, 0x02, 0x9c, - 0x2f, 0x03, 0x4e, 0xba, 0x0f, 0x48, 0x2f, 0x03, 0x4e, 0xba, 0xfd, 0x18, 0x4e, 0xba, 0xfd, 0x24, 0x2d, 0x40, 0xff, 0xf6, 0x20, 0x38, 0x02, 0xae, 0x50, 0x80, 0x24, 0x40, 0x1b, 0x52, 0xfc, 0x70, - 0x50, 0x8f, 0x66, 0x06, 0x42, 0x2d, 0xfc, 0x74, 0x60, 0x14, 0x1b, 0x7c, 0x00, 0x01, 0xfc, 0x74, 0x0c, 0x2d, 0x00, 0x03, 0xfc, 0x70, 0x65, 0x06, 0x1b, 0x7c, 0x00, 0x02, 0xfc, 0x70, 0x42, 0x6d, - 0xfc, 0x88, 0x42, 0xa7, 0x4e, 0xba, 0xfc, 0xf4, 0x42, 0x6d, 0xfc, 0x8c, 0x42, 0x2d, 0xfc, 0x78, 0x78, 0x00, 0x58, 0x8f, 0x60, 0x0e, 0x20, 0x04, 0xd0, 0x80, 0x41, 0xed, 0xfc, 0x90, 0x42, 0x70, - 0x08, 0x00, 0x52, 0x84, 0x70, 0x08, 0xb0, 0x84, 0x62, 0x00, 0xff, 0xec, 0x42, 0x2d, 0xfc, 0x7c, 0x36, 0x2b, 0x00, 0x18, 0x46, 0x43, 0x04, 0x43, 0x00, 0x20, 0x72, 0x00, 0x32, 0x03, 0x20, 0x01, - 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x45, 0xed, 0xfc, 0xa0, 0xd5, 0xc1, 0x4e, 0xba, 0xfc, 0x94, 0x27, 0x40, 0x00, 0x14, 0x42, 0xa7, - 0x70, 0x00, 0x30, 0x03, 0x2f, 0x00, 0x4e, 0xba, 0x07, 0x62, 0x42, 0x46, 0x42, 0x6e, 0xff, 0xfe, 0x7a, 0x01, 0x2d, 0x45, 0xff, 0xfa, 0x78, 0x01, 0x50, 0x8f, 0x60, 0x00, 0x01, 0x50, 0x48, 0x6e, - 0xfd, 0xf6, 0x2f, 0x04, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x70, 0x00, 0x30, 0x03, 0x2f, 0x00, 0x4e, 0xba, 0x08, 0x58, 0x3a, 0x00, 0x4f, 0xef, 0x00, 0x14, 0x67, 0x08, 0x0c, 0x45, 0xff, 0xfc, - 0x66, 0x00, 0x01, 0x28, 0x0c, 0x6e, 0x50, 0x4d, 0xfd, 0xf6, 0x66, 0x00, 0x01, 0x1e, 0x70, 0x01, 0xb0, 0x84, 0x66, 0x06, 0x2d, 0x6e, 0xfd, 0xfa, 0xff, 0xfa, 0x4a, 0x6e, 0xff, 0xfe, 0x66, 0x4e, - 0x41, 0xee, 0xfe, 0x26, 0x0c, 0x90, 0x41, 0x70, 0x70, 0x6c, 0x66, 0x42, 0x41, 0xee, 0xfe, 0x2a, 0x0c, 0x90, 0x65, 0x5f, 0x44, 0x72, 0x66, 0x36, 0x0c, 0xae, 0x00, 0x01, 0x06, 0x00, 0xfe, 0x7e, - 0x66, 0x2c, 0x72, 0x01, 0x3d, 0x41, 0xff, 0xfe, 0x70, 0x00, 0x30, 0x03, 0xd0, 0x80, 0x41, 0xed, 0xfc, 0x90, 0x31, 0xae, 0xfe, 0x8c, 0x08, 0x00, 0x35, 0x6e, 0xfe, 0x84, 0x00, 0x2c, 0x15, 0x6e, - 0xfe, 0x89, 0x00, 0x25, 0x15, 0x6e, 0xfe, 0x8b, 0x00, 0x27, 0x60, 0x00, 0x00, 0xbe, 0x4a, 0x46, 0x66, 0x00, 0x00, 0xb8, 0x41, 0xee, 0xfe, 0x26, 0x0c, 0x90, 0x41, 0x70, 0x70, 0x6c, 0x66, 0x00, - 0x00, 0xaa, 0x41, 0xee, 0xfe, 0x2a, 0x0c, 0x90, 0x65, 0x5f, 0x48, 0x46, 0x66, 0x00, 0x00, 0x9c, 0x7c, 0x01, 0x7a, 0x08, 0x20, 0x78, 0x03, 0x0a, 0x60, 0x0e, 0x30, 0x28, 0x00, 0x06, 0xb0, 0x45, - 0x66, 0x04, 0x52, 0x45, 0x60, 0xee, 0x20, 0x50, 0x20, 0x08, 0x66, 0x00, 0xff, 0xee, 0x42, 0x52, 0x08, 0x2e, 0x00, 0x05, 0xfe, 0x51, 0x67, 0x04, 0x70, 0x00, 0x60, 0x06, 0x20, 0x3c, 0x00, 0x00, - 0x00, 0x80, 0x15, 0x40, 0x00, 0x02, 0x15, 0x7c, 0x00, 0x08, 0x00, 0x03, 0x15, 0x7c, 0x00, 0x01, 0x00, 0x04, 0x42, 0x2a, 0x00, 0x05, 0x35, 0x7c, 0x00, 0x01, 0x00, 0x0a, 0x42, 0xaa, 0x00, 0x06, - 0x42, 0x6a, 0x00, 0x10, 0x35, 0x6e, 0xfe, 0x4c, 0x00, 0x12, 0x20, 0x2e, 0xfe, 0x4a, 0x72, 0x10, 0xe2, 0xa8, 0x35, 0x40, 0x00, 0x14, 0x20, 0x2e, 0xfe, 0x46, 0xd0, 0xae, 0xfd, 0xfe, 0x25, 0x40, - 0x00, 0x18, 0x25, 0x40, 0x00, 0x1c, 0x25, 0x6e, 0xfe, 0x4a, 0x00, 0x20, 0x35, 0x45, 0x00, 0x16, 0x48, 0x6a, 0x00, 0x06, 0x70, 0x00, 0x30, 0x05, 0x2f, 0x00, 0x30, 0x2b, 0x00, 0x18, 0x48, 0xc0, - 0x2f, 0x00, 0x4e, 0xba, 0x0c, 0x98, 0x4f, 0xef, 0x00, 0x0c, 0x52, 0x84, 0xb8, 0xae, 0xff, 0xfa, 0x63, 0x00, 0xfe, 0xac, 0x4a, 0x6e, 0xff, 0xfe, 0x66, 0x06, 0x7a, 0xfe, 0x60, 0x00, 0x00, 0x6e, - 0x4a, 0x46, 0x66, 0x04, 0x42, 0x45, 0x60, 0x64, 0x70, 0x00, 0x30, 0x03, 0x41, 0xed, 0xfc, 0x80, 0x11, 0xbc, 0x00, 0x01, 0x08, 0x00, 0x70, 0x01, 0x12, 0x03, 0xe3, 0xa0, 0x36, 0x00, 0x4a, 0x82, - 0x67, 0x06, 0x30, 0x03, 0x81, 0x6d, 0xfc, 0x8c, 0x4a, 0x6d, 0xfc, 0x88, 0x66, 0x28, 0x3b, 0x7c, 0x00, 0x01, 0xfc, 0x64, 0x41, 0xfa, 0x04, 0x58, 0x2b, 0x48, 0xfc, 0x66, 0x3b, 0x7c, 0x00, 0x65, - 0xfc, 0x6a, 0x42, 0x6d, 0xfc, 0x6c, 0x48, 0x6d, 0xfc, 0x60, 0x4e, 0xba, 0x0c, 0xc0, 0x1b, 0x7c, 0x00, 0x01, 0xfc, 0x7c, 0x58, 0x8f, 0x30, 0x03, 0x81, 0x6d, 0xfc, 0x88, 0x70, 0x00, 0x30, 0x2d, - 0xfc, 0x88, 0x2f, 0x00, 0x4e, 0xba, 0xfa, 0xb4, 0x42, 0x45, 0x58, 0x8f, 0x2f, 0x2e, 0xff, 0xf6, 0x4e, 0xba, 0x0c, 0xe4, 0x30, 0x05, 0x48, 0xc0, 0x58, 0x8f, 0x4c, 0xee, 0x0c, 0x7c, 0xfd, 0xd8, - 0x4e, 0x5e, 0x4e, 0x75, 0x48, 0xe7, 0x38, 0x20, 0x24, 0x6f, 0x00, 0x14, 0x20, 0x2f, 0x00, 0x18, 0x4e, 0xba, 0xfa, 0x80, 0x28, 0x00, 0x34, 0x2a, 0x00, 0x18, 0x46, 0x42, 0x04, 0x42, 0x00, 0x20, - 0x70, 0x01, 0x12, 0x02, 0xe3, 0xa0, 0x46, 0x40, 0xc1, 0x6d, 0xfc, 0x88, 0x70, 0x00, 0x30, 0x2d, 0xfc, 0x88, 0x2f, 0x00, 0x4e, 0xba, 0xfa, 0x64, 0x36, 0x2d, 0xfc, 0x88, 0x48, 0x78, 0x03, 0x08, - 0x32, 0x02, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0x48, 0x70, 0x18, 0x06, 0x4e, 0xba, - 0x0c, 0x04, 0x4a, 0x43, 0x4f, 0xef, 0x00, 0x0c, 0x66, 0x22, 0x4a, 0x2d, 0xfc, 0x7c, 0x67, 0x0a, 0x48, 0x6d, 0xfc, 0x60, 0x4e, 0xba, 0x0c, 0x20, 0x58, 0x8f, 0x4a, 0x2d, 0xfc, 0x78, 0x67, 0x0c, - 0x45, 0xfa, 0x04, 0x40, 0x2f, 0x0a, 0x4e, 0xba, 0xfc, 0x78, 0x58, 0x8f, 0x2f, 0x04, 0x4e, 0xba, 0x0c, 0x46, 0x4a, 0x43, 0x58, 0x8f, 0x66, 0x2a, 0x4e, 0xba, 0x0b, 0x7e, 0x24, 0x00, 0x4e, 0xba, - 0x0b, 0x88, 0x2f, 0x00, 0x4e, 0xba, 0x0b, 0x78, 0x4e, 0xba, 0xf9, 0xe2, 0x2f, 0x00, 0x4e, 0xba, 0x0c, 0x3c, 0x2f, 0x02, 0x4e, 0xba, 0x0b, 0x68, 0x42, 0xa7, 0x4e, 0xba, 0xf9, 0xc6, 0x4f, 0xef, - 0x00, 0x10, 0x70, 0x00, 0x4c, 0xdf, 0x04, 0x1c, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf0, 0x48, 0xe7, 0x3e, 0x38, 0x24, 0x2e, 0x00, 0x08, 0x24, 0x6e, 0x00, 0x0c, 0x4e, 0xba, 0xf9, 0xb6, 0x2d, 0x40, - 0xff, 0xf2, 0x28, 0x42, 0x3c, 0x2c, 0x00, 0x18, 0x46, 0x46, 0x04, 0x46, 0x00, 0x20, 0x32, 0x06, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, - 0x42, 0x40, 0xd2, 0x80, 0x47, 0xed, 0xfc, 0xa0, 0xd7, 0xc1, 0x78, 0x00, 0x0c, 0x2a, 0x00, 0x03, 0x00, 0x07, 0x57, 0xc4, 0x44, 0x04, 0x28, 0x42, 0x2d, 0x6c, 0x00, 0x10, 0xff, 0xfc, 0x70, 0x09, - 0x26, 0x2e, 0xff, 0xfc, 0xe0, 0xa3, 0x2d, 0x43, 0xff, 0xfc, 0x2d, 0x6a, 0x00, 0x24, 0xff, 0xf8, 0x70, 0x09, 0x26, 0x2e, 0xff, 0xf8, 0xe0, 0xa3, 0x2d, 0x43, 0xff, 0xf8, 0x30, 0x2b, 0x00, 0x16, - 0xb0, 0x6a, 0x00, 0x16, 0x67, 0x06, 0x76, 0xc8, 0x60, 0x00, 0x00, 0xe4, 0x20, 0x2a, 0x00, 0x24, 0x02, 0x80, 0x00, 0x00, 0x01, 0xff, 0x66, 0x00, 0x00, 0x18, 0x4a, 0xab, 0x00, 0x1c, 0x67, 0x16, - 0x20, 0x2b, 0x00, 0x20, 0x22, 0x2e, 0xff, 0xf8, 0xd2, 0xae, 0xff, 0xfc, 0xb0, 0x81, 0x64, 0x06, 0x76, 0xce, 0x60, 0x00, 0x00, 0xba, 0x30, 0x06, 0x48, 0xc0, 0x41, 0xed, 0xfc, 0x80, 0x4a, 0x30, - 0x08, 0x00, 0x67, 0x20, 0x30, 0x06, 0x48, 0xc0, 0x41, 0xed, 0xfc, 0x80, 0x42, 0x30, 0x08, 0x00, 0x70, 0x00, 0x10, 0x2d, 0xfc, 0x74, 0x2f, 0x00, 0x30, 0x06, 0x48, 0xc0, 0x2f, 0x00, 0x4e, 0xba, - 0x03, 0xca, 0x50, 0x8f, 0x08, 0x2a, 0x00, 0x06, 0x00, 0x2d, 0x67, 0x0a, 0x4a, 0x44, 0x66, 0x06, 0x42, 0x43, 0x60, 0x00, 0x00, 0x6c, 0x42, 0x6e, 0xff, 0xf6, 0x7a, 0xff, 0x2f, 0x2a, 0x00, 0x20, - 0x20, 0x2b, 0x00, 0x1c, 0xd0, 0xae, 0xff, 0xfc, 0x2f, 0x00, 0x2f, 0x2e, 0xff, 0xf8, 0x30, 0x04, 0x48, 0xc0, 0x2f, 0x00, 0x30, 0x06, 0x48, 0xc0, 0x2f, 0x00, 0x4e, 0xba, 0x04, 0xae, 0x36, 0x00, - 0x4f, 0xef, 0x00, 0x14, 0x67, 0x3a, 0x53, 0x43, 0x66, 0x10, 0x2f, 0x2b, 0x00, 0x28, 0x30, 0x06, 0x48, 0xc0, 0x2f, 0x00, 0x4e, 0xba, 0x07, 0x42, 0x50, 0x8f, 0xba, 0xab, 0x00, 0x28, 0x66, 0x06, - 0x52, 0x6e, 0xff, 0xf6, 0x60, 0x08, 0x42, 0x6e, 0xff, 0xf6, 0x2a, 0x2b, 0x00, 0x28, 0x0c, 0x6e, 0x00, 0x04, 0xff, 0xf6, 0x6d, 0x00, 0xff, 0xa6, 0x76, 0xdc, 0x42, 0xaa, 0x00, 0x28, 0x60, 0x0e, - 0x20, 0x2a, 0x00, 0x24, 0x25, 0x40, 0x00, 0x28, 0x20, 0x42, 0xd1, 0xa8, 0x00, 0x10, 0x2f, 0x2e, 0xff, 0xf2, 0x4e, 0xba, 0x0a, 0xa2, 0x30, 0x03, 0x48, 0xc0, 0x58, 0x8f, 0x4c, 0xee, 0x1c, 0x7c, - 0xff, 0xd0, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xfd, 0xfc, 0x48, 0xe7, 0x3c, 0x30, 0x26, 0x6e, 0x00, 0x08, 0x24, 0x6e, 0x00, 0x0c, 0x4e, 0xba, 0xf8, 0x3a, 0x2a, 0x00, 0x38, 0x2b, 0x00, 0x18, - 0x46, 0x44, 0x04, 0x44, 0x00, 0x20, 0x30, 0x04, 0x48, 0xc0, 0x24, 0x00, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x42, 0xc4, 0xfc, 0x00, 0x6c, 0x48, 0x42, 0x42, 0x42, 0xd0, 0x82, 0x41, 0xed, 0xfc, 0xa0, - 0xd1, 0xc0, 0x42, 0x43, 0x0c, 0x6a, 0x00, 0x41, 0x00, 0x1a, 0x66, 0x44, 0x4a, 0x2d, 0xfc, 0x78, 0x66, 0x34, 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x9f, 0x4e, 0xba, 0x09, 0xda, 0x24, 0x00, - 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x95, 0x4e, 0xba, 0x09, 0xcc, 0xb4, 0x80, 0x4f, 0xef, 0x00, 0x10, 0x67, 0x12, 0x45, 0xfa, 0x02, 0x0c, 0x2f, 0x0a, 0x4e, 0xba, 0xfa, 0x2c, 0x1b, 0x7c, - 0x00, 0x01, 0xfc, 0x78, 0x58, 0x8f, 0x02, 0x6b, 0x4f, 0xff, 0x00, 0x04, 0x60, 0x00, 0x00, 0xb2, 0x30, 0x28, 0x00, 0x16, 0xb0, 0x6a, 0x00, 0x16, 0x67, 0x06, 0x76, 0xc8, 0x60, 0x00, 0x00, 0xa2, - 0x30, 0x2a, 0x00, 0x1a, 0x0c, 0x40, 0x00, 0x07, 0x6d, 0x06, 0x6e, 0x1e, 0x60, 0x00, 0x00, 0x7e, 0x0c, 0x40, 0x00, 0x05, 0x6d, 0x00, 0x00, 0x88, 0x6e, 0x04, 0x60, 0x00, 0x00, 0x84, 0x0c, 0x40, - 0x00, 0x06, 0x66, 0x00, 0x00, 0x7a, 0x60, 0x00, 0x00, 0x78, 0x0c, 0x40, 0x00, 0x11, 0x6d, 0x00, 0x00, 0x6e, 0x6e, 0x02, 0x60, 0x08, 0x0c, 0x40, 0x00, 0x15, 0x66, 0x62, 0x60, 0x18, 0x43, 0xea, - 0x00, 0x1c, 0x0c, 0x51, 0x00, 0x01, 0x66, 0x06, 0x42, 0xa8, 0x00, 0x1c, 0x60, 0x52, 0x21, 0x68, 0x00, 0x18, 0x00, 0x1c, 0x60, 0x4a, 0x4e, 0xba, 0xf7, 0x6c, 0x2d, 0x40, 0xff, 0xfc, 0x20, 0x2e, - 0xff, 0xfc, 0x06, 0x80, 0x00, 0x00, 0x01, 0x00, 0x5c, 0x80, 0x32, 0x04, 0x48, 0xc1, 0x74, 0x30, 0xd2, 0x82, 0x26, 0x40, 0x16, 0x81, 0x48, 0x78, 0x00, 0x04, 0x48, 0x6e, 0xff, 0xfc, 0x48, 0x6a, - 0x00, 0x1c, 0x4e, 0xba, 0x08, 0x02, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x14, 0x70, 0x00, 0x30, 0x28, 0x00, 0x16, 0x2f, 0x00, 0x48, 0x78, 0x00, 0x07, 0x4e, 0xba, 0x08, 0xdc, 0x50, 0x8f, 0x76, 0xef, - 0x2f, 0x05, 0x4e, 0xba, 0x09, 0x52, 0x34, 0x03, 0x48, 0xc2, 0x58, 0x8f, 0x20, 0x02, 0x4c, 0xee, 0x0c, 0x3c, 0xfd, 0xe4, 0x4e, 0x5e, 0x4e, 0x75, 0x48, 0xe7, 0x30, 0x30, 0x26, 0x6f, 0x00, 0x14, - 0x24, 0x6f, 0x00, 0x18, 0x42, 0x42, 0x4e, 0xba, 0xf6, 0xea, 0x26, 0x00, 0x32, 0x2b, 0x00, 0x18, 0x46, 0x41, 0x04, 0x41, 0x00, 0x20, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, - 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0xd1, 0xc1, 0x30, 0x28, 0x00, 0x16, 0xb0, 0x6a, 0x00, 0x16, 0x67, 0x04, 0x74, 0xc8, 0x60, 0x1e, 0x30, 0x2a, - 0x00, 0x1a, 0x51, 0x40, 0x66, 0x14, 0x48, 0x78, 0x00, 0x16, 0x48, 0x50, 0x48, 0x6a, 0x00, 0x1c, 0x4e, 0xba, 0x07, 0x74, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x02, 0x74, 0xee, 0x2f, 0x03, 0x4e, 0xba, - 0x08, 0xd6, 0x30, 0x02, 0x48, 0xc0, 0x58, 0x8f, 0x4c, 0xdf, 0x0c, 0x0c, 0x4e, 0x75, 0x48, 0xe7, 0x38, 0x00, 0x4e, 0xba, 0xf6, 0x7e, 0x28, 0x00, 0x3b, 0x7c, 0x00, 0x65, 0xfc, 0x6a, 0x4a, 0x6d, - 0xfc, 0x8c, 0x67, 0x58, 0x36, 0x3c, 0x00, 0x80, 0x74, 0x07, 0x72, 0x00, 0x32, 0x2d, 0xfc, 0x8c, 0x30, 0x03, 0x48, 0xc0, 0xc2, 0x80, 0x67, 0x38, 0x32, 0x02, 0x48, 0xc1, 0x20, 0x01, 0xc2, 0xfc, - 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0x70, 0x00, 0x30, 0x30, 0x18, 0x16, 0x2f, 0x00, 0x48, 0x78, 0x00, 0x07, 0x4e, 0xba, - 0x07, 0xf6, 0x4a, 0x80, 0x50, 0x8f, 0x66, 0x08, 0x30, 0x03, 0x46, 0x40, 0xc1, 0x6d, 0xfc, 0x8c, 0x30, 0x03, 0xe2, 0x40, 0x36, 0x00, 0x53, 0x42, 0x6c, 0x00, 0xff, 0xb0, 0x4a, 0x2d, 0xfc, 0x74, - 0x66, 0x32, 0x22, 0x38, 0x02, 0xae, 0x20, 0x38, 0x0c, 0x54, 0x02, 0x80, 0x00, 0xff, 0xff, 0xff, 0xb2, 0x80, 0x63, 0x20, 0x1b, 0x7c, 0x00, 0x01, 0xfc, 0x74, 0x42, 0x42, 0x30, 0x02, 0x48, 0xc0, - 0x41, 0xed, 0xfc, 0x80, 0x11, 0xbc, 0x00, 0x01, 0x08, 0x00, 0x52, 0x42, 0x0c, 0x42, 0x00, 0x08, 0x6d, 0x00, 0xff, 0xea, 0x2f, 0x04, 0x4e, 0xba, 0x08, 0x1e, 0x58, 0x8f, 0x4c, 0xdf, 0x00, 0x1c, - 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf8, 0x48, 0xe7, 0x3c, 0x00, 0x4e, 0xba, 0xf5, 0xc6, 0x2a, 0x00, 0x48, 0x78, 0x00, 0x06, 0x48, 0x78, 0x00, 0x1b, 0x48, 0x6e, 0xff, 0xfa, 0x4e, 0xba, 0x06, 0xa2, - 0x70, 0x01, 0x12, 0x2d, 0xfc, 0x70, 0xe3, 0xa0, 0x38, 0x00, 0x74, 0x00, 0x4f, 0xef, 0x00, 0x0c, 0x20, 0x02, 0xd0, 0x80, 0x41, 0xed, 0xfc, 0x90, 0x36, 0x30, 0x08, 0x00, 0x70, 0x00, 0x30, 0x04, - 0x72, 0x00, 0x32, 0x03, 0xc0, 0x81, 0x67, 0x46, 0x4a, 0x82, 0x67, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x30, 0x03, 0x02, 0x80, 0x00, 0x00, 0x40, 0x00, 0x66, 0x34, 0x70, 0x00, 0x30, 0x03, 0x02, 0x80, - 0x00, 0x00, 0x80, 0x00, 0x67, 0x08, 0x1d, 0x7c, 0x00, 0x01, 0xff, 0xfb, 0x60, 0x04, 0x42, 0x2e, 0xff, 0xfb, 0x48, 0x78, 0x03, 0x84, 0x42, 0xa7, 0x42, 0xa7, 0x42, 0xa7, 0x2f, 0x02, 0x48, 0x78, - 0x00, 0x06, 0x48, 0x6e, 0xff, 0xfa, 0x4e, 0xba, 0x02, 0x70, 0x4f, 0xef, 0x00, 0x1c, 0x52, 0x82, 0x70, 0x08, 0xb0, 0x82, 0x6e, 0x00, 0xff, 0x9a, 0x2f, 0x05, 0x4e, 0xba, 0x07, 0x7a, 0x58, 0x8f, - 0x4c, 0xee, 0x00, 0x3c, 0xff, 0xe8, 0x4e, 0x5e, 0x4e, 0x75, 0x48, 0xe7, 0x30, 0x00, 0x22, 0x2f, 0x00, 0x0c, 0x26, 0x2f, 0x00, 0x10, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0xc0, 0xfc, - 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x41, 0xed, 0xfc, 0xa0, 0xd1, 0xc1, 0x22, 0x48, 0x70, 0x01, 0xb0, 0x83, 0x66, 0x1a, 0x70, 0x00, 0x10, 0x29, 0x00, 0x27, 0x72, 0x01, 0x14, 0x2d, - 0xfc, 0x70, 0xe5, 0xa1, 0xc0, 0x81, 0x67, 0x08, 0x13, 0x7c, 0x00, 0x01, 0x00, 0x26, 0x60, 0x04, 0x42, 0x29, 0x00, 0x26, 0x33, 0x7c, 0x00, 0x01, 0x00, 0x30, 0x4a, 0x83, 0x67, 0x00, 0x00, 0x08, - 0x4a, 0x29, 0x00, 0x25, 0x66, 0x16, 0x42, 0x29, 0x00, 0x24, 0x23, 0x7c, 0x00, 0x00, 0x02, 0x00, 0x00, 0x36, 0x33, 0x7c, 0x00, 0x07, 0x00, 0x3a, 0x60, 0x00, 0x00, 0xaa, 0x13, 0x69, 0x00, 0x25, - 0x00, 0x24, 0x70, 0x00, 0x10, 0x29, 0x00, 0x24, 0x0c, 0x40, 0x00, 0x01, 0x6d, 0x00, 0x00, 0x96, 0x6e, 0x02, 0x60, 0x08, 0x55, 0x40, 0x66, 0x00, 0x00, 0x8c, 0x60, 0x22, 0x23, 0x7c, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x36, 0x33, 0x7c, 0x00, 0x05, 0x00, 0x3a, 0x70, 0xf6, 0x23, 0x40, 0x00, 0x3c, 0x33, 0x7c, 0x00, 0x07, 0x00, 0x44, 0x33, 0x7c, 0x00, 0x01, 0x00, 0x2e, 0x60, 0x66, 0x70, 0x00, - 0x30, 0x29, 0x00, 0x2c, 0x23, 0x40, 0x00, 0x36, 0x33, 0x7c, 0x00, 0x04, 0x00, 0x3a, 0x41, 0xe9, 0x00, 0x32, 0x23, 0x48, 0x00, 0x3c, 0x41, 0xe9, 0x00, 0x46, 0x23, 0x48, 0x00, 0x40, 0x33, 0x7c, - 0x00, 0x01, 0x00, 0x44, 0x20, 0x3c, 0x00, 0x00, 0x02, 0x00, 0x72, 0x00, 0x32, 0x29, 0x00, 0x2c, 0x90, 0x81, 0x23, 0x40, 0x00, 0x4a, 0x33, 0x7c, 0x00, 0x04, 0x00, 0x4e, 0x41, 0xe9, 0x00, 0x46, - 0x23, 0x48, 0x00, 0x50, 0x41, 0xe9, 0x00, 0x32, 0x23, 0x48, 0x00, 0x54, 0x33, 0x7c, 0x00, 0x05, 0x00, 0x58, 0x70, 0xd8, 0x23, 0x40, 0x00, 0x5a, 0x33, 0x7c, 0x00, 0x07, 0x00, 0x62, 0x33, 0x7c, - 0x00, 0x04, 0x00, 0x2e, 0x4c, 0xdf, 0x00, 0x0c, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf0, 0x48, 0xe7, 0x3c, 0x20, 0x38, 0x2e, 0x00, 0x0a, 0x36, 0x2e, 0x00, 0x0e, 0x24, 0x2e, 0x00, 0x10, 0x4a, 0x43, - 0x67, 0x04, 0x70, 0x0a, 0x60, 0x02, 0x70, 0x08, 0x48, 0x78, 0x00, 0x06, 0x2f, 0x00, 0x48, 0x6e, 0xff, 0xf2, 0x4e, 0xba, 0x04, 0xcc, 0x72, 0x00, 0x32, 0x04, 0x20, 0x01, 0xc2, 0xfc, 0x00, 0x6c, - 0x48, 0x40, 0xc0, 0xfc, 0x00, 0x6c, 0x48, 0x40, 0x42, 0x40, 0xd2, 0x80, 0x45, 0xed, 0xfc, 0xa0, 0xd5, 0xc1, 0x25, 0x6e, 0x00, 0x18, 0x00, 0x32, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x00, 0x00, 0xc8, - 0x20, 0x2e, 0x00, 0x14, 0x72, 0x10, 0xe2, 0xa8, 0x1d, 0x40, 0xff, 0xf3, 0x41, 0xee, 0xff, 0xf4, 0x30, 0xae, 0x00, 0x16, 0x4a, 0x2a, 0x00, 0x24, 0x66, 0x04, 0x7a, 0x01, 0x60, 0x28, 0x0c, 0x82, - 0x00, 0x00, 0x01, 0x00, 0x63, 0x08, 0x2a, 0x3c, 0x00, 0x00, 0x01, 0x00, 0x60, 0x02, 0x2a, 0x02, 0x72, 0x00, 0x32, 0x2a, 0x00, 0x2e, 0xd2, 0x81, 0x20, 0x01, 0xe5, 0x81, 0xd2, 0x80, 0x20, 0x05, - 0x2a, 0x00, 0x25, 0x80, 0x18, 0x36, 0x1d, 0x45, 0xff, 0xf6, 0x48, 0x78, 0x2a, 0x30, 0x70, 0x00, 0x10, 0x2a, 0x00, 0x26, 0x2f, 0x00, 0x70, 0x00, 0x30, 0x03, 0x2f, 0x00, 0x48, 0x6a, 0x00, 0x30, - 0x70, 0x00, 0x30, 0x04, 0x2f, 0x00, 0x48, 0x78, 0x00, 0x06, 0x48, 0x6e, 0xff, 0xf2, 0x4e, 0xba, 0x00, 0x68, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x1c, 0x67, 0x44, 0x0c, 0x40, 0x00, 0x02, 0x67, 0x04, - 0x48, 0xc0, 0x60, 0x4a, 0x48, 0x78, 0x00, 0x08, 0x48, 0x6e, 0xff, 0xf8, 0x70, 0x00, 0x30, 0x04, 0x2f, 0x00, 0x4e, 0xba, 0x01, 0x58, 0x4a, 0x80, 0x4f, 0xef, 0x00, 0x0c, 0x67, 0x04, 0x70, 0xf9, - 0x60, 0x2c, 0x48, 0x78, 0x00, 0x04, 0x48, 0x6e, 0xff, 0xfb, 0x48, 0x6a, 0x00, 0x28, 0x4e, 0xba, 0x03, 0xd6, 0x70, 0x00, 0x10, 0x2e, 0xff, 0xfa, 0x4f, 0xef, 0x00, 0x0c, 0x60, 0x10, 0x94, 0x85, - 0x20, 0x05, 0xd1, 0xae, 0x00, 0x14, 0x4a, 0x82, 0x62, 0x00, 0xff, 0x36, 0x70, 0x00, 0x4c, 0xee, 0x04, 0x3c, 0xff, 0xdc, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf8, 0x48, 0xe7, 0x3e, 0x00, - 0x26, 0x2e, 0x00, 0x08, 0x38, 0x2e, 0x00, 0x0e, 0x3a, 0x2e, 0x00, 0x12, 0x42, 0x46, 0x42, 0x6e, 0xff, 0xfe, 0x55, 0x8f, 0x4e, 0xba, 0x03, 0xcc, 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x1e, - 0x52, 0x6e, 0xff, 0xfe, 0x0c, 0x6e, 0x00, 0x03, 0xff, 0xfe, 0x6f, 0x00, 0xff, 0xe6, 0x55, 0x8f, 0x4e, 0xba, 0x04, 0x00, 0x30, 0x1f, 0x08, 0x00, 0x00, 0x06, 0x67, 0xd2, 0x60, 0xf0, 0x55, 0x8f, - 0x3f, 0x05, 0x4e, 0xba, 0x03, 0xa8, 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x06, 0x70, 0xfe, 0x60, 0x00, 0x00, 0xb0, 0x42, 0x6e, 0xff, 0xfe, 0x55, 0x8f, 0x2f, 0x03, 0x3f, 0x04, 0x4e, 0xba, - 0x03, 0x96, 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x12, 0x52, 0x6e, 0xff, 0xfe, 0x0c, 0x6e, 0x00, 0x03, 0xff, 0xfe, 0x6f, 0x00, 0xff, 0xe2, 0x7c, 0xfd, 0x60, 0x52, 0x4a, 0xae, 0x00, 0x14, - 0x67, 0x4c, 0x4a, 0x6e, 0x00, 0x1e, 0x67, 0x24, 0x4a, 0x6e, 0x00, 0x1a, 0x67, 0x0c, 0x55, 0x8f, 0x2f, 0x2e, 0x00, 0x14, 0x4e, 0xba, 0x03, 0x92, 0x60, 0x0a, 0x55, 0x8f, 0x2f, 0x2e, 0x00, 0x14, - 0x4e, 0xba, 0x03, 0x7c, 0x30, 0x1f, 0x48, 0xc0, 0x2c, 0x00, 0x60, 0x22, 0x4a, 0x6e, 0x00, 0x1a, 0x67, 0x0c, 0x55, 0x8f, 0x2f, 0x2e, 0x00, 0x14, 0x4e, 0xba, 0x03, 0x5a, 0x60, 0x0a, 0x55, 0x8f, - 0x2f, 0x2e, 0x00, 0x14, 0x4e, 0xba, 0x03, 0x44, 0x30, 0x1f, 0x48, 0xc0, 0x2c, 0x00, 0x55, 0x8f, 0x48, 0x6e, 0xff, 0xfa, 0x48, 0x6e, 0xff, 0xfc, 0x2f, 0x2e, 0x00, 0x20, 0x4e, 0xba, 0x03, 0x22, - 0x30, 0x1f, 0x48, 0xc0, 0x30, 0x00, 0x67, 0x04, 0x70, 0xfc, 0x60, 0x16, 0x4a, 0x46, 0x67, 0x0c, 0x0c, 0x6e, 0x00, 0x02, 0xff, 0xfa, 0x67, 0x04, 0x30, 0x06, 0x60, 0x04, 0x30, 0x2e, 0xff, 0xfa, - 0x48, 0xc0, 0x4c, 0xee, 0x00, 0x7c, 0xff, 0xe4, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xe4, 0x48, 0xe7, 0x38, 0x00, 0x36, 0x2e, 0x00, 0x0a, 0x28, 0x2e, 0x00, 0x0c, 0x34, 0x2e, 0x00, 0x12, - 0x48, 0x78, 0x00, 0x06, 0x48, 0x78, 0x00, 0x03, 0x48, 0x6e, 0xff, 0xe6, 0x4e, 0xba, 0x02, 0x92, 0x1d, 0x42, 0xff, 0xea, 0x3d, 0x7c, 0x00, 0x02, 0xff, 0xec, 0x2d, 0x44, 0xff, 0xee, 0x70, 0x00, - 0x30, 0x02, 0x2d, 0x40, 0xff, 0xf2, 0x3d, 0x7c, 0x00, 0x07, 0xff, 0xf6, 0x48, 0x78, 0x02, 0x58, 0x42, 0xa7, 0x42, 0xa7, 0x48, 0x6e, 0xff, 0xec, 0x30, 0x03, 0x48, 0xc0, 0x2f, 0x00, 0x48, 0x78, - 0x00, 0x06, 0x48, 0x6e, 0xff, 0xe6, 0x4e, 0xba, 0xfe, 0x90, 0x4f, 0xef, 0x00, 0x28, 0x4c, 0xee, 0x00, 0x1c, 0xff, 0xd8, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xf4, 0x48, 0xe7, 0x3e, 0x30, - 0x24, 0x2e, 0x00, 0x08, 0x26, 0x2e, 0x00, 0x0c, 0x4e, 0xba, 0x02, 0xce, 0x2d, 0x40, 0xff, 0xf6, 0x4e, 0xba, 0x02, 0xd6, 0x2f, 0x00, 0x4e, 0xba, 0x02, 0xc6, 0x48, 0x78, 0x04, 0x00, 0x4e, 0xba, - 0x02, 0xd8, 0x24, 0x40, 0xb4, 0xfc, 0x00, 0x00, 0x50, 0x8f, 0x66, 0x1e, 0x4e, 0xba, 0x02, 0xc2, 0x2f, 0x00, 0x4e, 0xba, 0x02, 0xaa, 0x48, 0x78, 0x04, 0x00, 0x4e, 0xba, 0x02, 0xbc, 0x24, 0x40, - 0xb4, 0xfc, 0x00, 0x00, 0x50, 0x8f, 0x67, 0x00, 0x01, 0x16, 0x2f, 0x0a, 0x4e, 0xba, 0x02, 0xbe, 0x26, 0x52, 0x20, 0x4b, 0x41, 0xe8, 0x02, 0x00, 0x2d, 0x48, 0xff, 0xfc, 0x42, 0x6e, 0xff, 0xfa, - 0x58, 0x8f, 0x42, 0x45, 0x42, 0x46, 0x42, 0x44, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0xd2, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, - 0x67, 0x20, 0x53, 0x40, 0x66, 0x18, 0x52, 0x46, 0xba, 0x44, 0x66, 0x12, 0x48, 0x78, 0x02, 0x00, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x0b, 0x4e, 0xba, 0x01, 0x8e, 0x4f, 0xef, 0x00, 0x0c, 0x52, 0x45, - 0x60, 0x12, 0x48, 0x78, 0x02, 0x00, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x0b, 0x4e, 0xba, 0x01, 0x78, 0x4f, 0xef, 0x00, 0x0c, 0x52, 0x44, 0x0c, 0x44, 0x00, 0x0a, 0x6d, 0x00, 0xff, 0xac, 0x4a, 0x45, - 0x67, 0x00, 0x00, 0x9c, 0x4a, 0x46, 0x67, 0x00, 0x00, 0x96, 0x42, 0x44, 0x2f, 0x0b, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x01, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0x6e, 0x30, 0x00, - 0x4f, 0xef, 0x00, 0x14, 0x66, 0x00, 0x00, 0x28, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0x52, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, - 0x66, 0x00, 0x00, 0x0c, 0x52, 0x44, 0x0c, 0x44, 0x00, 0x0a, 0x6c, 0x52, 0x60, 0xbe, 0x52, 0x6e, 0xff, 0xfa, 0x0c, 0x6e, 0x00, 0x02, 0xff, 0xfa, 0x6e, 0x00, 0x00, 0x44, 0x2f, 0x03, 0x2f, 0x02, - 0x4e, 0xba, 0x00, 0x56, 0x50, 0x8f, 0x2f, 0x0b, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x48, 0x78, 0x00, 0x01, 0x2f, 0x02, 0x4e, 0xba, 0xfc, 0x14, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, 0x66, 0x00, - 0x00, 0x1e, 0x2f, 0x2e, 0xff, 0xfc, 0x2f, 0x03, 0x48, 0x78, 0x00, 0x01, 0x42, 0xa7, 0x2f, 0x02, 0x4e, 0xba, 0xfb, 0xf8, 0x30, 0x00, 0x4f, 0xef, 0x00, 0x14, 0x66, 0x00, 0xff, 0x06, 0x2f, 0x0a, - 0x4e, 0xba, 0x01, 0xa0, 0x2f, 0x2e, 0xff, 0xf6, 0x4e, 0xba, 0x01, 0x74, 0x50, 0x8f, 0x4c, 0xee, 0x0c, 0x7c, 0xff, 0xd8, 0x4e, 0x5e, 0x4e, 0x75, 0x4e, 0x56, 0xff, 0xd4, 0x48, 0xe7, 0x30, 0x00, - 0x34, 0x2e, 0x00, 0x0a, 0x26, 0x2e, 0x00, 0x0c, 0x48, 0x78, 0x00, 0x06, 0x48, 0x78, 0x00, 0x07, 0x48, 0x6e, 0xff, 0xde, 0x4e, 0xba, 0x00, 0xaa, 0x48, 0x78, 0x00, 0x08, 0x42, 0xa7, 0x48, 0x6e, - 0xff, 0xd6, 0x4e, 0xba, 0x00, 0x9c, 0x1d, 0x7c, 0x00, 0x04, 0xff, 0xd9, 0x3d, 0x7c, 0x00, 0x02, 0xff, 0xec, 0x41, 0xee, 0xff, 0xd6, 0x2d, 0x48, 0xff, 0xee, 0x70, 0x08, 0x2d, 0x40, 0xff, 0xf2, - 0x3d, 0x7c, 0x00, 0x07, 0xff, 0xf6, 0x41, 0xee, 0xff, 0xda, 0x20, 0x83, 0x48, 0x78, 0x0e, 0x10, 0x42, 0xa7, 0x48, 0x78, 0x00, 0x01, 0x48, 0x6e, 0xff, 0xec, 0x30, 0x02, 0x48, 0xc0, 0x2f, 0x00, - 0x48, 0x78, 0x00, 0x06, 0x48, 0x6e, 0xff, 0xde, 0x4e, 0xba, 0xfc, 0x8e, 0x4a, 0x80, 0x4f, 0xef, 0x00, 0x34, 0x67, 0x26, 0x48, 0x78, 0x00, 0x08, 0x48, 0x6e, 0xff, 0xe4, 0x30, 0x02, 0x48, 0xc0, - 0x2f, 0x00, 0x4e, 0xba, 0xfd, 0x88, 0x4a, 0x80, 0x4f, 0xef, 0x00, 0x0c, 0x67, 0x04, 0x70, 0xff, 0x60, 0x0a, 0x70, 0x00, 0x10, 0x2e, 0xff, 0xe6, 0x60, 0x02, 0x70, 0x00, 0x4c, 0xee, 0x00, 0x0c, - 0xff, 0xcc, 0x4e, 0x5e, 0x4e, 0x75, 0x20, 0x6f, 0x00, 0x04, 0x22, 0x6f, 0x00, 0x08, 0x20, 0x2f, 0x00, 0x0c, 0x60, 0x04, 0x10, 0xd9, 0x53, 0x80, 0x4a, 0x80, 0x66, 0x00, 0xff, 0xf8, 0x4e, 0x75, - 0x22, 0x6f, 0x00, 0x04, 0x10, 0x2f, 0x00, 0x0b, 0x32, 0x2f, 0x00, 0x0e, 0x48, 0xc1, 0x60, 0x06, 0x20, 0x41, 0xd1, 0xc9, 0x42, 0x10, 0x53, 0x81, 0x4a, 0x81, 0x6c, 0x00, 0xff, 0xf4, 0x12, 0x80, - 0x4e, 0x75, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x01, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x02, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x03, 0x2f, 0x08, 0xac, 0x15, - 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x04, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x05, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x06, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, - 0x3f, 0x3c, 0x00, 0x08, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x09, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x5f, 0x3f, 0x3c, 0x00, 0x0a, 0x2f, 0x08, 0xac, 0x15, 0x20, 0x6f, 0x00, 0x0c, - 0x30, 0x2f, 0x00, 0x0a, 0x48, 0x40, 0x30, 0x2f, 0x00, 0x06, 0xa0, 0x4e, 0x4e, 0x75, 0x70, 0x00, 0x31, 0xc0, 0x02, 0x20, 0x20, 0x08, 0x4e, 0x75, 0xa1, 0x1a, 0x4e, 0xfa, 0xff, 0xf4, 0x20, 0x6f, - 0x00, 0x04, 0xa0, 0x1b, 0x4e, 0xfa, 0xff, 0xea, 0x20, 0x78, 0x02, 0xa6, 0x4e, 0xfa, 0xff, 0xe0, 0x20, 0x78, 0x02, 0xaa, 0x4e, 0xfa, 0xff, 0xd8, 0x20, 0x2f, 0x00, 0x04, 0xa1, 0x22, 0x4e, 0xfa, - 0xff, 0xd0, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x23, 0x4e, 0xfa, 0xff, 0xc6, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x29, 0x4e, 0xfa, 0xff, 0xbc, 0x30, 0x6f, 0x00, 0x06, 0x20, 0x2f, 0x00, 0x08, 0xa0, 0x2f, - 0x48, 0xc0, 0x4e, 0x75, 0x22, 0x6f, 0x00, 0x08, 0x20, 0x6f, 0x00, 0x04, 0x2f, 0x02, 0xa9, 0x6e, 0x24, 0x1f, 0x48, 0xc0, 0x4e, 0x75, 0x30, 0x2f, 0x00, 0x06, 0x12, 0x2f, 0x00, 0x0b, 0x4a, 0x01, - 0x67, 0x04, 0xa7, 0x46, 0x60, 0x02, 0xa3, 0x46, 0x20, 0x08, 0x4e, 0x75, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x33, 0x48, 0xc0, 0x4e, 0x75, 0x20, 0x6f, 0x00, 0x04, 0xa0, 0x34, 0x48, 0xc0, 0x4e, 0x75, - 0x4e, 0xba, 0x00, 0x58, 0x06, 0x80, 0x00, 0x00, 0x00, 0x20, 0x4e, 0x75, 0x2f, 0x0d, 0x20, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x66, 0x0c, 0x20, 0x6f, 0x00, 0x08, 0x70, 0x07, 0x20, 0xdd, 0x51, 0xc8, - 0xff, 0xfc, 0x2a, 0x6f, 0x00, 0x08, 0x4e, 0xba, 0x00, 0x3a, 0x2a, 0x5f, 0x4e, 0x75, 0x20, 0x0d, 0x2a, 0x6f, 0x00, 0x04, 0x4e, 0x75, 0x2a, 0x6f, 0x00, 0x04, 0x4e, 0x75, 0x4e, 0xba, 0xff, 0xc2, - 0xa1, 0x1e, 0x2f, 0x08, 0x4e, 0xba, 0x00, 0x14, 0xd0, 0x9f, 0x4e, 0x75, 0x4e, 0xba, 0x00, 0x0c, 0x20, 0x6f, 0x00, 0x04, 0x91, 0xc0, 0xa0, 0x1f, 0x4e, 0x75, 0x41, 0xfa, 0x00, 0xce, 0x20, 0x10, - 0x4e, 0x75, 0x48, 0xe7, 0xff, 0xf0, 0x42, 0x47, 0x41, 0xfa, 0x00, 0xc0, 0x22, 0x18, 0x6f, 0x00, 0x00, 0xa6, 0x2a, 0x18, 0x66, 0x04, 0x2a, 0x0d, 0x9a, 0x81, 0x26, 0x45, 0x24, 0x4b, 0xe2, 0x81, - 0x60, 0x02, 0x42, 0x5a, 0x51, 0xc9, 0xff, 0xfc, 0x30, 0x18, 0x3e, 0x18, 0x60, 0x00, 0x00, 0x84, 0x78, 0x00, 0x18, 0x18, 0x22, 0x04, 0x02, 0x01, 0x00, 0x0f, 0x08, 0x04, 0x00, 0x04, 0x67, 0x0e, - 0xe1, 0x41, 0x12, 0x18, 0x08, 0x81, 0x00, 0x0b, 0x67, 0x04, 0xe1, 0x81, 0x12, 0x18, 0x74, 0x01, 0x08, 0x04, 0x00, 0x07, 0x67, 0x16, 0x14, 0x18, 0x08, 0x82, 0x00, 0x07, 0x67, 0x0e, 0xe1, 0x42, - 0x14, 0x18, 0x08, 0x82, 0x00, 0x0e, 0x67, 0x04, 0xe1, 0x82, 0x14, 0x18, 0x7c, 0x02, 0x4e, 0xba, 0x00, 0x4c, 0x08, 0x85, 0x00, 0x0f, 0x67, 0x04, 0xe1, 0x85, 0x1a, 0x18, 0x43, 0xf3, 0x58, 0x00, - 0x24, 0x49, 0x08, 0x04, 0x00, 0x05, 0x67, 0x1c, 0x2c, 0x01, 0x4e, 0xba, 0x00, 0x30, 0x0c, 0x41, 0x00, 0x02, 0x67, 0x06, 0x6d, 0x08, 0xdb, 0x92, 0x60, 0x0e, 0xdb, 0x52, 0x60, 0x0a, 0xdb, 0x12, - 0x60, 0x06, 0x12, 0xd8, 0x51, 0xc9, 0xff, 0xfc, 0x08, 0x04, 0x00, 0x06, 0x67, 0x04, 0x26, 0x0d, 0xd7, 0x92, 0x51, 0xc8, 0xff, 0x7c, 0x4c, 0xdf, 0x0f, 0xff, 0x4e, 0x75, 0x7a, 0x00, 0x60, 0x04, - 0xe1, 0x8d, 0x1a, 0x18, 0x51, 0xce, 0xff, 0xfa, 0x4e, 0x75, 0x00, 0x00, 0x03, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x8a, 0x25, 0x41, 0x35, 0x69, 0x80, 0x00, - 0x03, 0x7e, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x19, 0x1a, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x43, 0x53, 0x49, 0x20, 0x64, 0x72, 0x69, 0x76, - 0x65, 0x73, 0x2e, 0x2c, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x2e, 0x2e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, - }; - - const auto offset = (source_address - 0x40) * 512; - return std::vector(&driver[offset], &driver[offset + 512]); - } - - // Default: return an empty block. - return std::vector(512); +const char *VolumeProvider::driver_target() const { + return "68000"; +} + +const char *VolumeProvider::type() const { + return "Apple_HFS"; +} + +const char *VolumeProvider::name() const { + return "MacOS"; } diff --git a/Storage/MassStorage/Encodings/MacintoshVolume.hpp b/Storage/MassStorage/Encodings/MacintoshVolume.hpp index 5a41ee0df..930c03c84 100644 --- a/Storage/MassStorage/Encodings/MacintoshVolume.hpp +++ b/Storage/MassStorage/Encodings/MacintoshVolume.hpp @@ -12,16 +12,15 @@ #include #include #include -#include + +#include "ApplePartitionMap.hpp" namespace Storage { namespace MassStorage { namespace Encodings { namespace Macintosh { -enum class DriveType { - SCSI -}; +using DriveType = Storage::MassStorage::Encodings::Apple::DriveType; /*! On the Macintosh life is slightly complicated by Apple's @@ -39,47 +38,23 @@ class Volume { virtual void set_drive_type(DriveType type) = 0; }; +struct VolumeProvider { + static constexpr bool HasDriver = true; + size_t driver_size() const; + uint16_t driver_checksum() const; + const uint8_t *driver() const; + const char *driver_target() const; + + const char *name() const; + const char *type() const; +}; + /*! A Mapper can used by a mass-storage device that knows the - contents of an HFS or MFS partition to provide the conversion + contents of an HFS partition to provide the conversion necessary to a particular type of drive. */ -class Mapper { - public: - /*! - Sets the drive type to map to and the number of blocks in the underlying partition. - */ - void set_drive_type(DriveType, size_t number_of_blocks); - - /*! - Maps from a mass-storage device address to an address - in the underlying [H/M]FS partition. - */ - ssize_t to_source_address(size_t address); - - /*! - Converts from a source data block to one properly encoded for the drive type. - - Expected usage: - - const size_t source_address = mapper.to_source_address(unit_address); - if(is_in_range_for_partition(source_address)) { - return mapper.convert_source_block(source_address, get_block_contents(source_address)); - } else { - return mapper.convert_source_block(source_address); - } - */ - std::vector convert_source_block(ssize_t source_address, std::vector source_data = {}); - - /*! - @returns The total number of blocks on the entire volume. - */ - size_t get_number_of_blocks(); - - private: - DriveType drive_type_; - size_t number_of_blocks_; -}; +using Mapper = Storage::MassStorage::Encodings::Apple::PartitionMap; } } diff --git a/Storage/MassStorage/Formats/RawSectorDump.hpp b/Storage/MassStorage/Formats/RawSectorDump.hpp index 60d0e7314..cb7fc19d6 100644 --- a/Storage/MassStorage/Formats/RawSectorDump.hpp +++ b/Storage/MassStorage/Formats/RawSectorDump.hpp @@ -17,12 +17,15 @@ namespace Storage { namespace MassStorage { -template class RawSectorDump: public MassStorageDevice { +template class RawSectorDump: public MassStorageDevice { public: - RawSectorDump(const std::string &file_name) : file_(file_name) { + RawSectorDump(const std::string &file_name, long offset = 0, long length = -1) : + file_(file_name), + file_size_((length == -1) ? long(file_.stats().st_size) : length), + file_start_(offset) + { // Is the file a multiple of sector_size bytes in size? - const auto file_size = size_t(file_.stats().st_size); - if(file_size % sector_size) throw std::exception(); + if(file_size_ % sector_size) throw std::exception(); } /* MassStorageDevices overrides. */ @@ -31,22 +34,23 @@ template class RawSectorDump: public MassStorageDevice { } size_t get_number_of_blocks() final { - return size_t(file_.stats().st_size) / sector_size; + return size_t(file_size_ / sector_size); } std::vector get_block(size_t address) final { - file_.seek(long(address * sector_size), SEEK_SET); + file_.seek(file_start_ + long(address * sector_size), SEEK_SET); return file_.read(sector_size); } void set_block(size_t address, const std::vector &contents) final { assert(contents.size() == sector_size); - file_.seek(long(address * sector_size), SEEK_SET); + file_.seek(file_start_ + long(address * sector_size), SEEK_SET); file_.write(contents); } private: FileHolder file_; + const long file_size_, file_start_; }; } diff --git a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp index a6d99ccc2..4f77ed51d 100644 --- a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp +++ b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp @@ -55,6 +55,8 @@ bool DirectAccessDevice::write(const Target::CommandState &state, Target::Respon } bool DirectAccessDevice::read_capacity(const Target::CommandState &, Target::Responder &responder) { + if(!device_) return false; + const auto final_block = device_->get_number_of_blocks() - 1; const auto block_size = device_->get_block_size(); std::vector data = { @@ -81,6 +83,8 @@ Target::Executor::Inquiry DirectAccessDevice::inquiry_values() { } bool DirectAccessDevice::format_unit(const Target::CommandState &, Target::Responder &responder) { + if(!device_) return false; + // Formatting: immediate. responder.terminate_command(Target::Responder::Status::Good); return true; diff --git a/Storage/MassStorage/SCSI/SCSI.cpp b/Storage/MassStorage/SCSI/SCSI.cpp index 8d7909879..d14bdec22 100644 --- a/Storage/MassStorage/SCSI/SCSI.cpp +++ b/Storage/MassStorage/SCSI/SCSI.cpp @@ -74,7 +74,7 @@ void Bus::set_activity_observer(Activity::Observer *observer) { activity_observer_->register_led("SCSI"); } -BusState Bus::get_state() { +BusState Bus::get_state() const { return state_; } diff --git a/Storage/MassStorage/SCSI/SCSI.hpp b/Storage/MassStorage/SCSI/SCSI.hpp index c896f7c3b..f1fc87ef5 100644 --- a/Storage/MassStorage/SCSI/SCSI.hpp +++ b/Storage/MassStorage/SCSI/SCSI.hpp @@ -19,6 +19,8 @@ namespace SCSI { +/// Provides the current state of the SCSI bus, being comprised of a bitwise combination +/// of zero or more of the @c BusState flags defined below. typedef int BusState; constexpr BusState DefaultBusState = 0; @@ -118,7 +120,7 @@ class Bus: public ClockingHint::Source, public Activity::Source { /*! @returns the current state of the bus. */ - BusState get_state(); + BusState get_state() const; struct Observer { /// Reports to an observer that the bus changed from a previous state to @c new_state,