Switch to free-function little/big endian file reading.

This commit is contained in:
Thomas Harte committed 2026-10-16 20:47:46 -04:00
1 parent 69b3eb1dff
commit eb6350d539
28 files changed
+390 -281

No files matched your search

@@ -102,8 +102,9 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(
};
// Check for a .COM by inspecting the header.
const auto type = FileType(file.get_le<uint16_t>());
const uint16_t size = file.get_le<uint16_t>();
using Storage::FileUtils::LittleEndian::get;
const auto type = FileType(get<uint16_t>(file));
const uint16_t size = get<uint16_t>(file);
// There are then 12 bytes of 0 padding that could be tested for.
if((type != FileType::COM && type != FileType::BAS) || size > file.stats().st_size - 16) {
@@ -7629,7 +7629,6 @@
"$(PROJECT_DIR)/../../",
);
LD_RUNPATH_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@@ -7655,7 +7654,6 @@
"$(PROJECT_DIR)/../../",
);
LD_RUNPATH_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 11.0;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -7713,7 +7711,7 @@
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../";
MACOSX_DEPLOYMENT_TARGET = 14.6;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -7771,7 +7769,7 @@
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../";
MACOSX_DEPLOYMENT_TARGET = 14.6;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
+2 -1
View File
@@ -15,7 +15,8 @@ using namespace Storage::Cartridge;
PRG::PRG(const std::filesystem::path &path) {
Storage::FileHolder file(path, FileMode::Read);
const auto loading_address = file.get_le<uint16_t>();
using Storage::FileUtils::LittleEndian::get;
const auto loading_address = get<uint16_t>(file);
if(loading_address != 0xa000) {
throw ErrorNotROM;
}
+7 -6
View File
@@ -33,17 +33,18 @@ Disk2MG::DiskOrMassStorageDevice Disk2MG::open(const std::filesystem::path &path
const auto creator = file.read(4);
// Grab the header size, version number and image format.
const auto header_size = file.get_le<uint16_t>();
const auto version = file.get_le<uint16_t>();
const auto format = file.get_le<uint32_t>();
const auto flags = file.get_le<uint32_t>();
using Storage::FileUtils::LittleEndian::get;
const auto header_size = get<uint16_t>(file);
const auto version = get<uint16_t>(file);
const auto format = get<uint32_t>(file);
const auto flags = get<uint32_t>(file);
// Skip the number of ProDOS blocks; this is surely implicit from the data size?
file.seek(4, Whence::CUR);
// Get the offset and size of the disk image data.
const auto data_start = file.get_le<uint32_t>();
auto data_size = file.get_le<uint32_t>();
const auto data_start = get<uint32_t>(file);
auto data_size = get<uint32_t>(file);
// Correct for the Sweet 16 emulator, which writes broken 2MGs.
if(!data_size && !memcmp(creator.data(), "WOOF", 4)) {
+7 -3
View File
@@ -20,6 +20,8 @@ using namespace Storage::Disk;
CPCDSK::CPCDSK(const std::filesystem::path &path) :
path_(path),
is_extended_(false) {
using Storage::FileUtils::LittleEndian::get;
FileHolder file(path);
is_read_only_ = file.is_known_read_only();
@@ -48,7 +50,7 @@ CPCDSK::CPCDSK(const std::filesystem::path &path) :
track_sizes.push_back(size_t(file.get()) << 8);
}
} else {
size_of_a_track = file.get_le<uint16_t>();
size_of_a_track = get<uint16_t>(file);
}
long file_offset = 0x100;
@@ -140,7 +142,7 @@ CPCDSK::CPCDSK(const std::filesystem::path &path) :
// sector was weak or fuzzy and that multiple samplings are provided. If the greater size
// is not an exact multiple then my reading of the documentation is that this is an invalid
// disk image.
std::size_t declared_data_size = file.get_le<uint16_t>();
std::size_t declared_data_size = get<uint16_t>(file);
if(declared_data_size != stored_data_size) {
if(declared_data_size > data_size) {
number_of_samplings = declared_data_size / data_size;
@@ -222,6 +224,8 @@ std::unique_ptr<Track> CPCDSK::track_at_position(::Storage::Disk::Track::Address
}
void CPCDSK::set_tracks(const std::map<Storage::Disk::Track::Address, std::unique_ptr<Storage::Disk::Track>> &tracks) {
using Storage::FileUtils::LittleEndian::put;
// Patch changed tracks into the disk image.
for(auto &pair: tracks) {
// Assume MFM for now; with extensions DSK can contain FM tracks.
@@ -368,7 +372,7 @@ void CPCDSK::set_tracks(const std::map<Storage::Disk::Track::Address, std::uniqu
for(auto &sample: sector.samples) {
data_size += sample.size();
}
output.put_le<uint16_t>(uint16_t(data_size));
put<uint16_t>(output, uint16_t(data_size));
}
// Move to next 256-byte boundary.
+11 -4
View File
@@ -16,7 +16,10 @@ using namespace Storage::Disk;
namespace {
std::unique_ptr<Storage::Encodings::MFM::Encoder> new_encoder(Storage::Disk::PCMSegment &segment, bool is_double_density) {
std::unique_ptr<Storage::Encodings::MFM::Encoder> new_encoder(
Storage::Disk::PCMSegment &segment,
const bool is_double_density
) {
std::unique_ptr<Storage::Encodings::MFM::Encoder> encoder;
if(is_double_density) {
@@ -34,6 +37,8 @@ std::unique_ptr<Storage::Encodings::MFM::Encoder> new_encoder(Storage::Disk::PCM
DMK::DMK(const std::filesystem::path &path) :
file_(path) {
using Storage::FileUtils::LittleEndian::get;
// Determine whether this DMK represents a read-only disk (whether intentionally,
// or by virtue of filesystem placement).
uint8_t read_only_byte = file_.get();
@@ -42,7 +47,7 @@ DMK::DMK(const std::filesystem::path &path) :
// Read track count and size.
head_position_count_ = int(file_.get());
track_length_ = long(file_.get_le<uint16_t>());
track_length_ = long(get<uint16_t>(file_));
// Track length must be at least 0x80, as that's the size of the IDAM
// table before track contents.
@@ -57,7 +62,7 @@ DMK::DMK(const std::filesystem::path &path) :
// Skip to the end of the header and check that this is
// "in the emulator's native format".
file_.seek(0xc, Whence::SET);
const auto format = file_.get_le<uint32_t>();
const auto format = get<uint32_t>(file_);
if(format) throw Error::InvalidFormat;
}
@@ -80,13 +85,15 @@ long DMK::get_file_offset_for_position(const Track::Address address) const {
}
std::unique_ptr<::Storage::Disk::Track> DMK::track_at_position(const ::Storage::Disk::Track::Address address) const {
using Storage::FileUtils::LittleEndian::get;
file_.seek(get_file_offset_for_position(address), Whence::SET);
// Read the IDAM table.
uint16_t idam_locations[64];
std::size_t idam_count = 0;
for(std::size_t c = 0; c < sizeof(idam_locations) / sizeof(*idam_locations); ++c) {
idam_locations[idam_count] = file_.get_le<uint16_t>();
idam_locations[idam_count] = get<uint16_t>(file_);
if((idam_locations[idam_count] & 0x7fff) >= 128) {
idam_count++;
}
+5 -4
View File
@@ -21,13 +21,14 @@ FAT12::FAT12(const std::filesystem::path &path) :
if(file_size < 512) throw Error::InvalidFormat;
// Inspect the FAT.
using Storage::FileUtils::LittleEndian::get;
file_.seek(11, Whence::SET);
sector_size_ = file_.get_le<uint16_t>();
sector_size_ = get<uint16_t>(file_);
file_.seek(19, Whence::SET);
const auto total_sectors = file_.get_le<uint16_t>();
const auto total_sectors = get<uint16_t>(file_);
file_.seek(24, Whence::SET);
sector_count_ = file_.get_le<uint16_t>();
head_count_ = file_.get_le<uint16_t>();
sector_count_ = get<uint16_t>(file_);
head_count_ = get<uint16_t>(file_);
// Throw if there would seemingly be an incomplete track.
if(file_size != total_sectors*sector_size_) throw Error::InvalidFormat;
+19 -16
View File
@@ -27,7 +27,8 @@ G64::G64(const std::filesystem::path &path) :
// get the number of tracks and track size
number_of_tracks_ = file_.get();
maximum_track_size_ = file_.get_le<uint16_t>();
using Storage::FileUtils::LittleEndian::get;
maximum_track_size_ = get<uint16_t>(file_);
}
HeadPosition G64::maximum_head_position() const {
@@ -37,40 +38,42 @@ HeadPosition G64::maximum_head_position() const {
}
std::unique_ptr<Track> G64::track_at_position(const Track::Address address) const {
// seek to this track's entry in the track table
using Storage::FileUtils::LittleEndian::get;
// Seek to this track's entry in the track table.
file_.seek(long((address.position.as_half() * 4) + 0xc), Whence::SET);
// read the track offset
const auto track_offset = file_.get_le<uint32_t>();
// Read the track offset.
const auto track_offset = get<uint32_t>(file_);
// if the track offset is zero, this track doesn't exist, so...
// If the track offset is zero, this track doesn't exist, so...
if(!track_offset) return nullptr;
// seek to the track start
// Seek to track start.
file_.seek(long(track_offset), Whence::SET);
// get the real track length
const auto track_length = file_.get_le<uint16_t>();
// Get the real track length.
const auto track_length = get<uint16_t>(file_);
// grab the byte contents of this track
// Grab the byte contents of this track.
const std::vector<uint8_t> track_contents = file_.read(track_length);
// seek to this track's entry in the speed zone table
// Seek to this track's entry in the speed zone table.
file_.seek(long((address.position.as_half() * 4) + 0x15c), Whence::SET);
// read the speed zone offsrt
const auto speed_zone_offset = file_.get_le<uint32_t>();
// Read the speed zone offset.
const auto speed_zone_offset = get<uint32_t>(file_);
// if the speed zone is not constant, create a track based on the whole table; otherwise create one that's constant
// If the speed zone is not constant, create a track based on the whole table; otherwise create one that's constant.
if(speed_zone_offset > 3) {
// seek to start of speed zone
// Seek to start of speed zone.
file_.seek(long(speed_zone_offset), Whence::SET);
// read the speed zone bytes
// Read the speed zone bytes.
const uint16_t speed_zone_length = (track_length + 3) >> 2;
const auto speed_zone_contents = file_.read(speed_zone_length);
// divide track into appropriately timed PCMSegments
// Divide track into appropriately timed PCMSegments.
std::vector<PCMSegment> segments;
unsigned int current_speed = speed_zone_contents[0] >> 6;
unsigned int start_byte_in_current_speed = 0;
+5 -3
View File
@@ -22,7 +22,8 @@ HFE::HFE(const std::filesystem::path &path) :
head_count_ = file_.get();
file_.seek(7, Whence::CUR);
track_list_offset_ = long(file_.get_le<uint16_t>()) << 9;
using Storage::FileUtils::LittleEndian::get;
track_list_offset_ = long(get<uint16_t>(file_)) << 9;
}
HeadPosition HFE::maximum_head_position() const {
@@ -45,8 +46,9 @@ uint16_t HFE::seek_track(const Track::Address address) const {
// based on an assumption of two heads.
file_.seek(track_list_offset_ + address.position.as_int() * 4, Whence::SET);
long track_offset = long(file_.get_le<uint16_t>()) << 9; // Track offset, in units of 512 bytes.
const auto track_length = file_.get_le<uint16_t>(); // Track length, in bytes, containing both the front and back track.
using Storage::FileUtils::LittleEndian::get;
long track_offset = long(get<uint16_t>(file_)) << 9; // Track offset, in units of 512 bytes.
const auto track_length = get<uint16_t>(file_); // Track length, in bytes, containing both the front and back track.
file_.seek(track_offset, Whence::SET);
if(address.head) file_.seek(256, Whence::CUR);
+28 -25
View File
@@ -37,15 +37,16 @@ size_t block_size(Storage::FileHolder &file, const uint8_t header) {
}
IPF::IPF(const std::filesystem::path &path) : file_(path) {
using Storage::FileUtils::BigEndian::get;
std::map<uint32_t, Track::Address> tracks_by_data_key;
// For now, just build up a list of tracks that exist, noting the file position at which their data begins
// plus the other fields that'll be necessary to convert them into flux on demand later.
while(true) {
const auto start_of_block = file_.tell();
const auto type = file_.get_be<uint32_t>();
auto length = file_.get_be<uint32_t>(); // Can't be const because of the dumb encoding of DATA blocks.
[[maybe_unused]] const auto crc = file_.get_be<uint32_t>();
const auto type = get<uint32_t>(file_);
auto length = get<uint32_t>(file_); // Can't be const because of the dumb encoding of DATA blocks.
[[maybe_unused]] const auto crc = get<uint32_t>(file_);
if(file_.eof()) break;
// Sanity check: the first thing in a file should be the CAPS record.
@@ -71,21 +72,21 @@ IPF::IPF(const std::filesystem::path &path) : file_(path) {
// aren't that interesting.
// Make sure this is a floppy disk.
const auto media_type = file_.get_be<uint32_t>();
const auto media_type = get<uint32_t>(file_);
if(media_type != 1) {
throw Error::InvalidFormat;
}
// Determine whether this is a newer SPS-style file.
is_sps_format_ = file_.get_be<uint32_t>() > 1;
is_sps_format_ = get<uint32_t>(file_) > 1;
// Skip: revision, file key and revision, CRC of the original .ctr, and minimum track.
file_.seek(20, Whence::CUR);
track_count_ = int(1 + file_.get_be<uint32_t>());
track_count_ = int(1 + get<uint32_t>(file_));
// Skip: min side.
file_.seek(4, Whence::CUR);
head_count_ = int(1 + file_.get_be<uint32_t>());
head_count_ = int(1 + get<uint32_t>(file_));
// Skip: creation date, time.
file_.seek(8, Whence::CUR);
@@ -117,8 +118,8 @@ IPF::IPF(const std::filesystem::path &path) : file_(path) {
case block("IMGE"): {
// Get track location.
const auto track = file_.get_be<uint32_t>();
const auto side = file_.get_be<uint32_t>();
const auto track = get<uint32_t>(file_);
const auto side = get<uint32_t>(file_);
const Track::Address address{int(side), HeadPosition(int(track))};
// Hence generate a TrackDescription.
@@ -128,31 +129,31 @@ IPF::IPF(const std::filesystem::path &path) : file_(path) {
// Read those fields of interest...
// Bit density. I've no idea why the density can't just be given as a measurement.
description.density = TrackDescription::Density(file_.get_be<uint32_t>());
description.density = TrackDescription::Density(get<uint32_t>(file_));
if(description.density > TrackDescription::Density::Max) {
description.density = TrackDescription::Density::Unknown;
}
file_.seek(12, Whence::CUR); // Skipped: signal type, track bytes, start byte position.
description.start_bit_pos = file_.get_be<uint32_t>();
description.data_bits = file_.get_be<uint32_t>();
description.gap_bits = file_.get_be<uint32_t>();
description.start_bit_pos = get<uint32_t>(file_);
description.data_bits = get<uint32_t>(file_);
description.gap_bits = get<uint32_t>(file_);
file_.seek(4, Whence::CUR); // Skipped: track bits, which is entirely redundant.
description.block_count = file_.get_be<uint32_t>();
description.block_count = get<uint32_t>(file_);
file_.seek(4, Whence::CUR); // Skipped: encoder process.
description.has_fuzzy_bits = file_.get_be<uint32_t>() & 1;
description.has_fuzzy_bits = get<uint32_t>(file_) & 1;
// For some reason the authors decided to introduce another primary key,
// in addition to that which naturally exists of (track, side). So set up
// a mapping from the one to the other.
const auto data_key = file_.get_be<uint32_t>();
const auto data_key = get<uint32_t>(file_);
tracks_by_data_key.emplace(data_key, address);
} break;
case block("DATA"): {
length += file_.get_be<uint32_t>();
length += get<uint32_t>(file_);
file_.seek(8, Whence::CUR); // Skipped: bit size, CRC.
@@ -160,7 +161,7 @@ IPF::IPF(const std::filesystem::path &path) : file_(path) {
// position for this track.
//
// Assumed here: DATA records will come after corresponding IMGE records.
const auto data_key = file_.get_be<uint32_t>();
const auto data_key = get<uint32_t>(file_);
const auto pair = tracks_by_data_key.find(data_key);
if(pair == tracks_by_data_key.end()) {
break;
@@ -187,6 +188,8 @@ int IPF::head_count() const {
}
std::unique_ptr<Track> IPF::track_at_position(const Track::Address address) const {
using Storage::FileUtils::BigEndian::get;
// Get the track description, if it exists, and check either that the file has contents for the track.
auto pair = tracks_.find(address);
if(pair == tracks_.end()) {
@@ -220,24 +223,24 @@ std::unique_ptr<Track> IPF::track_at_position(const Track::Address address) cons
blocks.reserve(description.block_count);
for(uint32_t c = 0; c < description.block_count; c++) {
auto &block = blocks.emplace_back();
block.data_bits = file_.get_be<uint32_t>();
block.gap_bits = file_.get_be<uint32_t>();
block.data_bits = get<uint32_t>(file_);
block.gap_bits = get<uint32_t>(file_);
if(is_sps_format_) {
block.gap_offset = file_.get_be<uint32_t>();
block.gap_offset = get<uint32_t>(file_);
file_.seek(4, Whence::CUR); // Skip 'cell type' which appears to provide no content.
} else {
// Skip potlower-resolution copies of data_bits and gap_bits.
file_.seek(8, Whence::CUR);
}
block.is_mfm = file_.get_be<uint32_t>() == 1;
block.is_mfm = get<uint32_t>(file_) == 1;
const auto flags = file_.get_be<uint32_t>();
const auto flags = get<uint32_t>(file_);
block.has_forward_gap = flags & 1;
block.has_backwards_gap = flags & 2;
block.data_unit_is_bits = flags & 4;
block.default_gap_value = file_.get_be<uint32_t>();
block.data_offset = file_.get_be<uint32_t>();
block.default_gap_value = get<uint32_t>(file_);
block.data_offset = get<uint32_t>(file_);
}
std::vector<Storage::Disk::PCMSegment> segments;
+8 -6
View File
@@ -106,7 +106,8 @@ MOOF::MOOF(const std::filesystem::path &path) :
}
// Test the file's CRC32.
const auto crc = file_.get_le<uint32_t>();
using Storage::FileUtils::LittleEndian::get;
const auto crc = get<uint32_t>(file_);
post_crc_contents_ = file_.read(size_t(file_.stats().st_size - 12));
const uint32_t computed_crc = CRC::CRC32::crc_of(post_crc_contents_);
if(crc != computed_crc) {
@@ -119,8 +120,8 @@ MOOF::MOOF(const std::filesystem::path &path) :
std::fill(std::begin(track_map_), std::end(track_map_), 0xff);
std::fill(std::begin(flux_map_), std::end(flux_map_), 0xff);
while(true) {
const auto chunk_id = file_.get_le<uint32_t>();
const auto chunk_size = file_.get_le<uint32_t>();
const auto chunk_id = get<uint32_t>(file_);
const auto chunk_size = get<uint32_t>(file_);
const auto end_of_chunk = file_.tell() + long(chunk_size);
if(file_.eof()) break;
@@ -184,6 +185,7 @@ int MOOF::head_count() const {
}
std::unique_ptr<Track> MOOF::track_at_position(const Track::Address address) const {
using Storage::FileUtils::LittleEndian::get;
const int table_position = address.position.as_int() * 2 + address.head;
if(table_position > 255) {
return nullptr;
@@ -192,9 +194,9 @@ std::unique_ptr<Track> MOOF::track_at_position(const Track::Address address) con
const auto location = [&](const uint8_t offset) -> TrackLocation {
file_.seek(tracks_offset_ + 8 * long(offset), Whence::SET);
TrackLocation location;
location.starting_block = file_.get_le<uint16_t>();
location.block_count = file_.get_le<uint16_t>();
location.bit_count = file_.get_le<uint32_t>();
location.starting_block = get<uint16_t>(file_);
location.block_count = get<uint16_t>(file_);
location.bit_count = get<uint32_t>(file_);
return location;
};
+9 -7
View File
@@ -16,17 +16,19 @@ using namespace Storage::Disk;
MSA::MSA(const std::filesystem::path &path) :
file_(path) {
const auto signature = file_.get_be<uint16_t>();
using FileUtils::BigEndian::get;
const auto signature = get<uint16_t>(file_);
if(signature != 0x0e0f) throw Error::InvalidFormat;
sectors_per_track_ = file_.get_be<uint16_t>();
sides_ = 1 + file_.get_be<uint16_t>();
starting_track_ = file_.get_be<uint16_t>();
ending_track_ = file_.get_be<uint16_t>();
sectors_per_track_ = get<uint16_t>(file_);
sides_ = 1 + get<uint16_t>(file_);
starting_track_ = get<uint16_t>(file_);
ending_track_ = get<uint16_t>(file_);
// Create the uncompressed track list.
while(true) {
const auto data_length = file_.get_be<uint16_t>();
const auto data_length = get<uint16_t>(file_);
if(file_.eof()) break;
if(data_length == sectors_per_track_ * 512) {
@@ -55,7 +57,7 @@ MSA::MSA(const std::filesystem::path &path) :
if(pointer > data_length) break;
const auto value = file_.get();
auto count = file_.get_be<uint16_t>();
auto count = get<uint16_t>(file_);
while(count--) {
track.push_back(value);
}
@@ -75,10 +75,11 @@ MacintoshIMG::MacintoshIMG(const std::filesystem::path &path) :
// Get the length of the data and tag blocks.
file_.seek(64, Whence::SET);
const auto data_block_length = file_.get_be<uint32_t>();
const auto tag_block_length = file_.get_be<uint32_t>();
const auto data_checksum = file_.get_be<uint32_t>();
const auto tag_checksum = file_.get_be<uint32_t>();
using Storage::FileUtils::BigEndian::get;
const auto data_block_length = get<uint32_t>(file_);
const auto tag_block_length = get<uint32_t>(file_);
const auto data_checksum = get<uint32_t>(file_);
const auto tag_checksum = get<uint32_t>(file_);
// Don't continue with no data.
if(!data_block_length) {
@@ -98,7 +99,7 @@ MacintoshIMG::MacintoshIMG(const std::filesystem::path &path) :
format_ = file_.get();
// Check the magic number.
const auto magic_number = file_.get_be<uint16_t>();
const auto magic_number = get<uint16_t>(file_);
if(magic_number != 0x0100)
throw Error::InvalidFormat;
@@ -332,8 +333,9 @@ void MacintoshIMG::set_tracks(const std::map<Track::Address, std::unique_ptr<Tra
const auto tag_checksum = checksum(tags_, 12);
file_.seek(0x48, Whence::SET);
file_.put_be(data_checksum);
file_.put_be(tag_checksum);
using Storage::FileUtils::BigEndian::put;
put(file_, data_checksum);
put(file_, tag_checksum);
}
}
}
@@ -21,9 +21,10 @@ OricMFMDSK::OricMFMDSK(const std::filesystem::path &path) :
if(!file_.check_signature<SignatureType::String>("MFM_DISK"))
throw Error::InvalidFormat;
head_count_ = file_.get_le<uint32_t>();
track_count_ = file_.get_le<uint32_t>();
geometry_type_ = file_.get_le<uint32_t>();
using Storage::FileUtils::LittleEndian::get;
head_count_ = get<uint32_t>(file_);
track_count_ = get<uint32_t>(file_);
geometry_type_ = get<uint32_t>(file_);
if(geometry_type_ < 1 || geometry_type_ > 2)
throw Error::InvalidFormat;
+1 -1
View File
@@ -91,7 +91,7 @@ std::unique_ptr<Track> SAP::track_at_position(const Track::Address address) cons
// Also it includes the four SAP-specific fields in its CRC so this definitely isn't supposed to be the one that
// appears on the imaged disk itself even if bit order were conventional.
const auto calculated = CRC::Generator<uint16_t, 0x1021, 0xffff, 0x0000, true, true>::crc_of(contents);
const auto crc = file_.get_be<uint16_t>();
const auto crc = FileUtils::get<std::endian::big, uint16_t>(file_);
contents.erase(contents.begin(), contents.begin() + 4);
sector.samples.push_back(contents);
+17 -13
View File
@@ -391,9 +391,11 @@ private:
}
STX::STX(const std::filesystem::path &path) : file_(path) {
using FileUtils::LittleEndian::get;
// Require that this be a version 3 Pasti.
if(!file_.check_signature<SignatureType::Binary>("RSY")) throw Error::InvalidFormat;
if(file_.get_le<uint16_t>() != 3) throw Error::InvalidFormat;
if(get<uint16_t>(file_) != 3) throw Error::InvalidFormat;
// Skip: tool used, 2 reserved bytes.
file_.seek(4, Whence::CUR);
@@ -423,7 +425,7 @@ STX::STX(const std::filesystem::path &path) : file_(path) {
head_count_ = 1;
while(true) {
const long offset = file_.tell();
const auto size = file_.get_le<uint32_t>();
const auto size = get<uint32_t>(file_);
if(file_.eof()) break;
// Skip fields other than track position, then fill in table position and advance.
@@ -461,10 +463,11 @@ std::unique_ptr<Track> STX::track_at_position(const Track::Address address) cons
file_.seek(offset_by_track_[track_index] + 4, Whence::SET);
// Grab the track description.
const auto fuzzy_size = file_.get_le<uint32_t>();
const auto sector_count = file_.get_le<uint16_t>();
const auto flags = file_.get_le<uint16_t>();
const size_t track_length = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
const auto fuzzy_size = get<uint32_t>(file_);
const auto sector_count = get<uint16_t>(file_);
const auto flags = get<uint16_t>(file_);
const size_t track_length = get<uint16_t>(file_);
file_.seek(2, Whence::CUR); // Skip track type; despite being named, it's apparently unused.
// If this is a trivial .ST-style sector dump, life is easy.
@@ -481,9 +484,9 @@ std::unique_ptr<Track> STX::track_at_position(const Track::Address address) cons
// Sector records come first.
for(uint16_t c = 0; c < sector_count; ++c) {
sectors.emplace_back();
sectors.back().data_offset = file_.get_le<uint32_t>();
sectors.back().bit_position = file_.get_le<uint16_t>();
sectors.back().data_duration = file_.get_le<uint16_t>();
sectors.back().data_offset = get<uint32_t>(file_);
sectors.back().bit_position = get<uint16_t>(file_);
sectors.back().data_duration = get<uint16_t>(file_);
file_.read(sectors.back().address);
sectors.back().status = file_.get();
file_.seek(1, Whence::CUR);
@@ -520,11 +523,11 @@ std::unique_ptr<Track> STX::track_at_position(const Track::Address address) cons
// Bit 6 => there is a track to read;
// bit
if(flags & 0x80) {
first_sync = file_.get_le<uint16_t>();
const auto image_size = file_.get_le<uint16_t>();
first_sync = get<uint16_t>(file_);
const auto image_size = get<uint16_t>(file_);
track_data = file_.read(image_size);
} else {
const auto image_size = file_.get_le<uint16_t>();
const auto image_size = get<uint16_t>(file_);
track_data = file_.read(image_size);
}
}
@@ -572,7 +575,8 @@ std::unique_ptr<Track> STX::track_at_position(const Track::Address address) cons
// This is going to be a new-format record.
for(size_t c = 0; c < timing_record_size; ++c) {
sector.timing[c] = file_.get_be<uint16_t>(); // These values are big endian, unlike the rest of the file.
// WARNING: these values are big endian, unlike the rest of the file.
sector.timing[c] = FileUtils::get<std::endian::big, uint16_t>(file_);
}
}
+12 -8
View File
@@ -24,6 +24,7 @@ constexpr uint32_t chunk(const char *str) {
WOZ::WOZ(const std::filesystem::path &path) :
file_(path) {
using FileUtils::LittleEndian::get;
static constexpr char signature1[8] = {
'W', 'O', 'Z', '1',
@@ -42,7 +43,7 @@ WOZ::WOZ(const std::filesystem::path &path) :
type_ = isWoz2 ? Type::WOZ2 : Type::WOZ1;
// Get the file's CRC32.
const auto crc = file_.get_le<uint32_t>();
const auto crc = get<uint32_t>(file_);
// Get the collection of all data that contributes to the CRC.
post_crc_contents_ = file_.read(size_t(file_.stats().st_size - 12));
@@ -59,8 +60,8 @@ WOZ::WOZ(const std::filesystem::path &path) :
// Parse all chunks up front.
bool has_tmap = false;
while(true) {
const auto chunk_id = file_.get_le<uint32_t>();
const auto chunk_size = file_.get_le<uint32_t>();
const auto chunk_id = get<uint32_t>(file_);
const auto chunk_size = get<uint32_t>(file_);
const auto end_of_chunk = file_.tell() + long(chunk_size);
if(file_.eof()) break;
@@ -148,6 +149,8 @@ bool WOZ::tracks_differ(const Track::Address lhs, const Track::Address rhs) cons
}
std::unique_ptr<Track> WOZ::track_at_position(const Track::Address address) const {
using FileUtils::LittleEndian::get;
const long offset = file_offset(address);
if(offset == NoSuchTrack) {
return nullptr;
@@ -167,15 +170,16 @@ std::unique_ptr<Track> WOZ::track_at_position(const Track::Address address) cons
// of bits that were used. Other information follows but is not intended for emulation.
track_contents = file_.read(6646);
file_.seek(2, Whence::CUR);
number_of_bits = std::min(file_.get_le<uint16_t>(), uint16_t(6646*8));
number_of_bits = std::min(get<uint16_t>(file_), uint16_t(6646*8));
break;
default:
case Type::WOZ2: {
// In WOZ 2 an extra level of indirection allows for variable track sizes.
const auto starting_block = file_.get_le<uint16_t>();
file_.seek(2, Whence::CUR); // Skip the block count; the amount of data to read is implied by the number of bits.
number_of_bits = file_.get_le<uint32_t>();
const auto starting_block = get<uint16_t>(file_);
file_.seek(2, Whence::CUR); // Skip the block count; the amount of data to read is implied
// by the number of bits.
number_of_bits = get<uint32_t>(file_);
file_.seek(starting_block * 512, Whence::SET);
track_contents = file_.read((number_of_bits + 7) >> 3);
@@ -214,7 +218,7 @@ void WOZ::set_tracks(const std::map<Track::Address, std::unique_ptr<Track>> &tra
// Grab the file lock, then write the CRC, then just dump the entire file buffer.
std::lock_guard lock_guard(file_.file_access_mutex());
file_.seek(8, Whence::SET);
file_.put_le(crc);
FileUtils::put<std::endian::little>(file_, crc);
file_.write(post_crc_contents_);
}
+118 -62
View File
@@ -12,6 +12,7 @@
#include <sys/stat.h>
#include <array>
#include <bit>
#include <cstdio>
#include <cstdint>
#include <cstring>
@@ -62,59 +63,6 @@ public:
FileHolder(const std::filesystem::path &, FileMode ideal_mode = FileMode::ReadWrite);
FileHolder(FileHolder &&);
/*!
Writes @c value using successive @c puts, in little endian order.
Optionally limits itself to only @c size bytes.
*/
template <typename IntT, size_t size = sizeof(IntT)>
void put_le(IntT value) {
for(size_t c = 0; c < size; c++) {
put(uint8_t(value));
value >>= 8;
}
}
/*!
Writes @c value using successive @c puts, in big endian order.
Optionally limits itself to only @c size bytes.
*/
template <typename IntT, size_t size = sizeof(IntT)>
void put_be(const IntT value) {
auto shift = size * 8;
while(shift) {
shift -= 8;
put((value >> shift)&0xff);
}
}
/*!
Reads a value of type @c IntT using successive @c gets, in little endian order.
Optionally limits itself to only @c size bytes.
*/
template <typename IntT, size_t size = sizeof(IntT)>
IntT get_le() {
IntT result{};
for(size_t c = 0; c < size; c++) {
result >>= 8;
result |= IntT(get() << ((size - 1) * 8));
}
return result;
}
/*!
Reads a value of type @c IntT using successive @c gets, in big endian order.
Optionally limits itself to only @c size bytes.
*/
template <typename IntT, size_t size = sizeof(IntT)>
IntT get_be() {
IntT result{};
for(size_t c = 0; c < size; c++) {
result <<= 8;
result |= get();
}
return result;
}
/*! Reads a single byte from @c file. */
uint8_t get();
@@ -156,15 +104,6 @@ public:
/*! @returns @c true if the end-of-file indicator is set, @c false otherwise. */
bool eof() const;
/*!
Obtains a BitStream for reading from the file from the current reading cursor.
*/
template <int max_bits, bool lsb_first>
Numeric::BitStream<max_bits, lsb_first> bitstream() {
return Numeric::BitStream<max_bits, lsb_first>([&] {
return get();
});
}
/*!
For SignatureType::Binary:
@@ -231,4 +170,121 @@ inline std::vector<uint8_t> contents_of(const std::filesystem::path &path) {
return file.read(size_t(file.stats().st_size));
}
/*!
Various free functions that work with an open file.
*/
namespace FileUtils {
/*!
Obtains a BitStream for reading from the file from the current reading cursor.
*/
template <int max_bits, bool lsb_first>
Numeric::BitStream<max_bits, lsb_first> bitstream(FileHolder &file) {
return Numeric::BitStream<max_bits, lsb_first>([&] {
return file.get();
});
}
/*!
A local implementation of std::byteswap, which I cannot currently use because it's C++23.
*/
template <typename IntT>
requires (sizeof(IntT) == 1 || sizeof(IntT) == 2 || sizeof(IntT) == 4 || sizeof(IntT) == 8)
IntT byteswap(IntT value) {
if constexpr (sizeof(IntT) == 1) {
return value;
}
if constexpr (sizeof(IntT) == 2) {
return __builtin_bswap16(value);
}
if constexpr (sizeof(IntT) == 4) {
return __builtin_bswap32(value);
}
if constexpr (sizeof(IntT) == 8) {
return __builtin_bswap64(value);
}
}
/*!
Writes @c value to @c file according to @c endian.
Optionally limits itself to only @c size bytes.
*/
template <std::endian endian, typename IntT, size_t size = sizeof(IntT)>
void put(FileHolder &file, IntT value) {
if constexpr (size != sizeof(IntT) && endian == std::endian::big) {
// Little on little: relevant bytes are the first ones.
// Little on big: low bytes were swapped into high. Still the first ones. Do nothing.
// Big on little: pull bytes up to top ready to be swapped into bottom.
// Big on bug: pull meaningful bytes up to the front.
value <<= ((sizeof(IntT) - size) / 8);
}
if constexpr (endian != std::endian::native && sizeof(IntT) > 1) {
value = byteswap<IntT>(value);
}
file.write(&value, size);
}
/*!
Reads a value of type @c IntT from @c file using @c endian byte order.
Optionally limits itself to only @c size bytes.
*/
template <std::endian endian, typename IntT, size_t size = sizeof(IntT)>
IntT get(FileHolder &file) {
IntT result;
if constexpr (size != sizeof(IntT)) {
result = 0;
}
file.read(reinterpret_cast<uint8_t *>(&result), size);
if constexpr (endian != std::endian::native) {
result = byteswap<IntT>(result);
}
if constexpr (size != sizeof(IntT) && endian == std::endian::big) {
// Little on little: read into low bytes. Do nothing.
// Little on big: read into high bytes, swapped into low. Do nothing.
// Big on little: read into low bytes, swapped into high. Shift downward.
// Big on big: read into high bytes. Shift downward.
result >>= ((sizeof(IntT) - size) / 8);
}
return result;
}
/*
Helpers below are designed to allow more-compact syntax elsewhere; e.g.
`using FileUtils::LittleEndian::get`.
*/
namespace LittleEndian {
template <std::unsigned_integral IntT, size_t size = sizeof(IntT)>
IntT get(Storage::FileHolder &file) {
return Storage::FileUtils::get<std::endian::little, IntT, size>(file);
}
template <typename IntT, size_t size = sizeof(IntT)>
void put(FileHolder &file, IntT value) {
return Storage::FileUtils::put<std::endian::little, IntT, size>(file, value);
}
}
namespace BigEndian {
template <std::unsigned_integral IntT, size_t size = sizeof(IntT)>
IntT get(Storage::FileHolder &file) {
return Storage::FileUtils::get<std::endian::big, IntT, size>(file);
}
template <typename IntT, size_t size = sizeof(IntT)>
void put(FileHolder &file, IntT value) {
return Storage::FileUtils::put<std::endian::big, IntT, size>(file, value);
}
}
}
}
+10 -8
View File
@@ -17,6 +17,8 @@ constexpr size_t SectorSize = 512;
}
VHD::VHD(const std::filesystem::path &path) : file_(path) {
using FileUtils::BigEndian::get;
// Find footer; this may be the final 511 or final 512 bytes of the file.
// Find what might be the start of the 'conectix' [sic] signature.
file_.seek(-511, Whence::END);
@@ -32,22 +34,22 @@ VHD::VHD(const std::filesystem::path &path) : file_(path) {
}
file_.seek(4, Whence::CUR); // Skip 'Features', which would at best classify this disk as temporary or not.
const auto major_version = file_.get_be<uint16_t>();
const auto major_version = get<uint16_t>(file_);
if(major_version > 1) {
throw std::exception();
}
file_.seek(2, Whence::CUR); // Skip minor version number.
data_offset_ = file_.get_be<uint64_t>();
data_offset_ = get<uint64_t>(file_);
file_.seek(32, Whence::CUR); // Skip creator and timestamp fields, original size and current size.
// const auto current_size = file_.get_be<uint64_t>();
// const auto current_size = get<uint64_t>();
cylinders_ = file_.get_be<uint16_t>();
cylinders_ = get<uint16_t>(file_);
heads_ = file_.get();
sides_ = file_.get();
switch(file_.get_be<uint32_t>()) {
switch(get<uint32_t>(file_)) {
case 2: type_ = Type::Fixed; break;
case 3: type_ = Type::Dynamic; break;
case 4: type_ = Type::Differencing; break;
@@ -67,11 +69,11 @@ VHD::VHD(const std::filesystem::path &path) : file_(path) {
}
file_.seek(8, Whence::CUR); // Skip data offset.
table_offset_ = file_.get_be<uint64_t>();
table_offset_ = get<uint64_t>(file_);
file_.seek(4, Whence::CUR); // Skip table version. TODO: validate.
max_table_entries_ = file_.get_be<uint32_t>();
block_size_ = file_.get_be<uint32_t>();
max_table_entries_ = get<uint32_t>(file_);
block_size_ = get<uint32_t>(file_);
total_blocks_ = block_size_ * max_table_entries_;
}
+13 -11
View File
@@ -45,18 +45,20 @@ std::unique_ptr<Analyser::Static::Target> SNA::load(const std::filesystem::path
// 00 I
const uint8_t i = file.get();
using FileUtils::LittleEndian::get;
// 01 HL'; 03 DE'; 05 BC'; 07 AF'
state->z80.registers.hl_dash = file.get_le<uint16_t>();
state->z80.registers.de_dash = file.get_le<uint16_t>();
state->z80.registers.bc_dash = file.get_le<uint16_t>();
state->z80.registers.af_dash = file.get_le<uint16_t>();
state->z80.registers.hl_dash = get<uint16_t>(file);
state->z80.registers.de_dash = get<uint16_t>(file);
state->z80.registers.bc_dash = get<uint16_t>(file);
state->z80.registers.af_dash = get<uint16_t>(file);
// 09 HL; 0B DE; 0D BC; 0F IY; 11 IX
state->z80.registers.hl = file.get_le<uint16_t>();
state->z80.registers.de = file.get_le<uint16_t>();
state->z80.registers.bc = file.get_le<uint16_t>();
state->z80.registers.iy = file.get_le<uint16_t>();
state->z80.registers.ix = file.get_le<uint16_t>();
state->z80.registers.hl = get<uint16_t>(file);
state->z80.registers.de = get<uint16_t>(file);
state->z80.registers.bc = get<uint16_t>(file);
state->z80.registers.iy = get<uint16_t>(file);
state->z80.registers.ix = get<uint16_t>(file);
// 13 IFF2 (in bit 2)
const uint8_t iff = file.get();
@@ -69,7 +71,7 @@ std::unique_ptr<Analyser::Static::Target> SNA::load(const std::filesystem::path
// 15 AF; 17 SP; 19 interrupt mode
state->z80.registers.flags = file.get();
state->z80.registers.a = file.get();
state->z80.registers.stack_pointer = file.get_le<uint16_t>();
state->z80.registers.stack_pointer = get<uint16_t>(file);
state->z80.registers.interrupt_mode = file.get();
// 1A border colour
@@ -87,7 +89,7 @@ std::unique_ptr<Analyser::Static::Target> SNA::load(const std::filesystem::path
return result;
}
state->z80.registers.program_counter = file.get_le<uint16_t>();
state->z80.registers.program_counter = get<uint16_t>(file);
state->last_7ffd = file.get();
const bool trdos_paged = file.get();
+17 -16
View File
@@ -71,9 +71,10 @@ std::unique_ptr<Analyser::Static::Target> SZX::load(const std::filesystem::path
[[maybe_unused]] const bool uses_late_timings = file_flags & 1;
// Now parse all included blocks.
using FileUtils::LittleEndian::get;
while(true) {
const auto blockID = file.get_le<uint32_t>();
const auto size = file.get_le<uint32_t>();
const auto blockID = get<uint32_t>(file);
const auto size = get<uint32_t>(file);
if(file.eof()) break;
const auto location = file.tell();
@@ -87,19 +88,19 @@ std::unique_ptr<Analyser::Static::Target> SZX::load(const std::filesystem::path
state->z80.registers.flags = file.get();
state->z80.registers.a = file.get();
state->z80.registers.bc = file.get_le<uint16_t>();
state->z80.registers.de = file.get_le<uint16_t>();
state->z80.registers.hl = file.get_le<uint16_t>();
state->z80.registers.bc = get<uint16_t>(file);
state->z80.registers.de = get<uint16_t>(file);
state->z80.registers.hl = get<uint16_t>(file);
state->z80.registers.af_dash = file.get_le<uint16_t>();
state->z80.registers.bc_dash = file.get_le<uint16_t>();
state->z80.registers.de_dash = file.get_le<uint16_t>();
state->z80.registers.hl_dash = file.get_le<uint16_t>();
state->z80.registers.af_dash = get<uint16_t>(file);
state->z80.registers.bc_dash = get<uint16_t>(file);
state->z80.registers.de_dash = get<uint16_t>(file);
state->z80.registers.hl_dash = get<uint16_t>(file);
state->z80.registers.ix = file.get_le<uint16_t>();
state->z80.registers.iy = file.get_le<uint16_t>();
state->z80.registers.stack_pointer = file.get_le<uint16_t>();
state->z80.registers.program_counter = file.get_le<uint16_t>();
state->z80.registers.ix = get<uint16_t>(file);
state->z80.registers.iy = get<uint16_t>(file);
state->z80.registers.stack_pointer = get<uint16_t>(file);
state->z80.registers.program_counter = get<uint16_t>(file);
const uint8_t i = file.get();
const uint8_t r = file.get();
@@ -109,7 +110,7 @@ std::unique_ptr<Analyser::Static::Target> SZX::load(const std::filesystem::path
state->z80.registers.iff2 = file.get();
state->z80.registers.interrupt_mode = file.get();
state->video.half_cycles_since_interrupt = int(file.get_le<uint32_t>()) * 2;
state->video.half_cycles_since_interrupt = int(get<uint32_t>(file)) * 2;
// SZX includes a count of remaining cycles that interrupt should be asserted for
// because it supports hardware that might cause an interrupt other than the display.
@@ -123,7 +124,7 @@ std::unique_ptr<Analyser::Static::Target> SZX::load(const std::filesystem::path
// verdict but I'm unclear what the effect of an invalid DD or FD is so
// have not yet implemented this.
state->z80.registers.memptr = file.get_le<uint16_t>();
state->z80.registers.memptr = get<uint16_t>(file);
} break;
// ZXSTAYBLOCK
@@ -138,7 +139,7 @@ std::unique_ptr<Analyser::Static::Target> SZX::load(const std::filesystem::path
// ZXSTRAMPAGE
case block("RAMP"): {
const uint16_t flags = file.get_le<uint16_t>();
const uint16_t flags = get<uint16_t>(file);
const uint8_t page = file.get();
std::vector<uint8_t> contents;
+17 -16
View File
@@ -63,6 +63,7 @@ std::vector<uint8_t> read_memory(Storage::FileHolder &file, const size_t size, c
std::unique_ptr<Analyser::Static::Target> Z80::load(const std::filesystem::path &path) {
FileHolder file(path);
using FileUtils::LittleEndian::get;
// Construct a target with a Spectrum state.
using Target = Analyser::Static::ZXSpectrum::Target;
@@ -73,11 +74,11 @@ std::unique_ptr<Analyser::Static::Target> Z80::load(const std::filesystem::path
// Read version 1 header.
state->z80.registers.a = file.get();
state->z80.registers.flags = file.get();
state->z80.registers.bc = file.get_le<uint16_t>();
state->z80.registers.hl = file.get_le<uint16_t>();
state->z80.registers.program_counter = file.get_le<uint16_t>();
state->z80.registers.stack_pointer = file.get_le<uint16_t>();
state->z80.registers.ir = file.get_be<uint16_t>(); // Stored I then R.
state->z80.registers.bc = get<uint16_t>(file);
state->z80.registers.hl = get<uint16_t>(file);
state->z80.registers.program_counter = get<uint16_t>(file);
state->z80.registers.stack_pointer = get<uint16_t>(file);
state->z80.registers.ir = FileUtils::get<std::endian::big, uint16_t>(file); // Stored I then R.
// Bit 7 of R is stored separately; likely this relates to an
// optimisation in the Z80 emulator that for some reason was
@@ -86,13 +87,13 @@ std::unique_ptr<Analyser::Static::Target> Z80::load(const std::filesystem::path
const uint8_t misc = (raw_misc == 0xff) ? 1 : raw_misc;
state->z80.registers.ir = uint16_t((state->z80.registers.ir & ~0x80) | ((misc&1) << 7));
state->z80.registers.de = file.get_le<uint16_t>();
state->z80.registers.bc_dash = file.get_le<uint16_t>();
state->z80.registers.de_dash = file.get_le<uint16_t>();
state->z80.registers.hl_dash = file.get_le<uint16_t>();
state->z80.registers.af_dash = file.get_be<uint16_t>(); // Stored A' then F'.
state->z80.registers.iy = file.get_le<uint16_t>();
state->z80.registers.ix = file.get_le<uint16_t>();
state->z80.registers.de = get<uint16_t>(file);
state->z80.registers.bc_dash = get<uint16_t>(file);
state->z80.registers.de_dash = get<uint16_t>(file);
state->z80.registers.hl_dash = get<uint16_t>(file);
state->z80.registers.af_dash = FileUtils::get<std::endian::big, uint16_t>(file); // Stored A' then F'.
state->z80.registers.iy = get<uint16_t>(file);
state->z80.registers.ix = get<uint16_t>(file);
state->z80.registers.iff1 = bool(file.get());
state->z80.registers.iff2 = bool(file.get());
@@ -113,12 +114,12 @@ std::unique_ptr<Analyser::Static::Target> Z80::load(const std::filesystem::path
}
// This was a version 1 or 2 snapshot, so keep going...
const auto bonus_header_size = file.get_le<uint16_t>();
const auto bonus_header_size = get<uint16_t>(file);
if(bonus_header_size != 23 && bonus_header_size != 54 && bonus_header_size != 55) {
return nullptr;
}
state->z80.registers.program_counter = file.get_le<uint16_t>();
state->z80.registers.program_counter = get<uint16_t>(file);
const uint8_t model = file.get();
switch(model) {
default: return nullptr;
@@ -148,7 +149,7 @@ std::unique_ptr<Analyser::Static::Target> Z80::load(const std::filesystem::path
if(bonus_header_size != 23) {
// More Z80, the emulator, lack of encapsulation to deal with here.
const auto low_t_state = file.get_le<uint16_t>();
const auto low_t_state = get<uint16_t>(file);
const uint16_t high_t_state = file.get();
switch(result->model) {
case Target::Model::SixteenK:
@@ -183,7 +184,7 @@ std::unique_ptr<Analyser::Static::Target> Z80::load(const std::filesystem::path
}
while(true) {
const auto block_size = file.get_le<uint16_t>();
const auto block_size = get<uint16_t>(file);
const uint8_t page = file.get();
const auto location = file.tell();
if(file.eof()) break;
+2 -2
View File
@@ -36,7 +36,7 @@ CSW::CSW(const std::filesystem::path &path) {
// The header now diverges based on version.
CompressionType compression_type;
if(major_version == 1) {
pulse_.length.clock_rate = file.get_le<uint16_t>();
pulse_.length.clock_rate = FileUtils::get<std::endian::little, uint16_t>(file);
if(file.get() != 1) throw ErrorNotCSW;
compression_type = CompressionType::RLE;
@@ -45,7 +45,7 @@ CSW::CSW(const std::filesystem::path &path) {
file.seek(0x20, Whence::SET);
} else {
pulse_.length.clock_rate = file.get_le<uint32_t>();
pulse_.length.clock_rate = FileUtils::get<std::endian::little, uint32_t>(file);
file.seek(4, Whence::CUR); // Skip number of waves.
switch(file.get()) {
case 1: compression_type = CompressionType::RLE; break;
+1 -1
View File
@@ -89,7 +89,7 @@ Storage::Tape::Pulse CommodoreTAP::Serialiser::next_pulse() {
if(!updated_layout_ || next_byte > 0) {
next_length = uint32_t(next_byte) << 3;
} else {
next_length = file_.get_le<uint32_t, 3>();
next_length = FileUtils::get<std::endian::little, uint32_t, 3>(file_);
}
if(file_.eof()) {
+4 -4
View File
@@ -114,7 +114,7 @@ MSXCAS::MSXCAS(const std::filesystem::path &path) {
} else {
// Raw data appears now. Grab its length and keep going.
file.seek(header_position + 8, Whence::SET);
const auto length = file.get_le<uint16_t>();
const auto length = FileUtils::get<std::endian::little, uint16_t>(file);
file.seek(header_position + 8, Whence::SET);
chunks_.emplace_back(false, false, file.read(size_t(length) + 2));
@@ -136,8 +136,8 @@ MSXCAS::MSXCAS(const std::filesystem::path &path) {
// Get the start and end addresses in order to figure out how much data
// is here.
file.seek(header_position + 8, Whence::SET);
const auto start_address = file.get_le<uint16_t>();
const auto end_address = file.get_le<uint16_t>();
const auto start_address = FileUtils::get<std::endian::little, uint16_t>(file);
const auto end_address = FileUtils::get<std::endian::little, uint16_t>(file);
file.seek(header_position + 8, Whence::SET);
const auto length = end_address - start_address + 1;
@@ -152,7 +152,7 @@ MSXCAS::MSXCAS(const std::filesystem::path &path) {
file.seek(header_position + 8, Whence::SET);
uint16_t address = 0x8001; // the BASIC start address.
while(true) {
const auto next_line_address = file.get_le<uint16_t>();
const auto next_line_address = FileUtils::get<std::endian::little, uint16_t>(file);
if(!next_line_address || file.eof()) break;
file.seek(next_line_address - address - 2, Whence::CUR);
address = next_line_address;
+56 -45
View File
@@ -108,11 +108,13 @@ void TZX::Serialiser::push_next_pulses() {
}
void TZX::Serialiser::get_csw_recording_block() {
const auto block_length = file_.get_le<uint32_t>();
const auto pause_after_block = file_.get_le<uint16_t>();
const auto sampling_rate = file_.get_le<uint32_t, 3>();
using FileUtils::LittleEndian::get;
const auto block_length = get<uint32_t>(file_);
const auto pause_after_block = get<uint16_t>(file_);
const auto sampling_rate = get<uint32_t, 3>(file_);
const auto compression_type = file_.get();
const auto number_of_compressed_pulses = file_.get_le<uint32_t>();
const auto number_of_compressed_pulses = get<uint32_t>(file_);
std::vector<uint8_t> raw_block = file_.read(block_length - 10);
@@ -134,15 +136,17 @@ void TZX::Serialiser::get_csw_recording_block() {
}
void TZX::Serialiser::get_generalised_data_block() {
const auto block_length = file_.get_le<uint32_t>();
const long endpoint = file_.tell() + long(block_length);
const auto pause_after_block = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
const auto total_pilot_symbols = file_.get_le<uint32_t>();
const auto block_length = get<uint32_t>(file_);
const long endpoint = file_.tell() + long(block_length);
const auto pause_after_block = get<uint16_t>(file_);
const auto total_pilot_symbols = get<uint32_t>(file_);
const uint8_t maximum_pulses_per_pilot_symbol = file_.get();
const uint8_t symbols_in_pilot_table = file_.get();
const auto total_data_symbols = file_.get_le<uint32_t>();
const auto total_data_symbols = get<uint32_t>(file_);
const uint8_t maximum_pulses_per_data_symbol = file_.get();
const uint8_t symbols_in_data_table = file_.get();
@@ -160,6 +164,7 @@ void TZX::Serialiser::get_generalised_segment(
const uint8_t number_of_symbols,
const bool is_data
) {
using FileUtils::LittleEndian::get;
if(!output_symbols) return;
// Construct the symbol table.
@@ -172,13 +177,13 @@ void TZX::Serialiser::get_generalised_segment(
Symbol symbol;
symbol.flags = file_.get();
for(int ic = 0; ic < max_pulses_per_symbol; ic++) {
symbol.pulse_lengths.push_back(file_.get_le<uint16_t>());
symbol.pulse_lengths.push_back(get<uint16_t>(file_));
}
symbol_table.push_back(symbol);
}
// Hence produce the output.
auto stream = file_.bitstream<8, false>();
auto stream = FileUtils::bitstream<8, false>(file_);
int base = 2;
size_t bits = 1;
while(base < number_of_symbols) {
@@ -193,7 +198,7 @@ void TZX::Serialiser::get_generalised_segment(
count = 1;
} else {
symbol_value = file_.get();
count = file_.get_le<uint16_t>();
count = get<uint16_t>(file_);
}
if(symbol_value > number_of_symbols) {
continue;
@@ -227,8 +232,9 @@ void TZX::Serialiser::get_standard_speed_data_block() {
data_block.data.length_of_one_bit_pulse = 1710;
data_block.data.number_of_bits_in_final_byte = 8;
data_block.data.pause_after_block = file_.get_le<uint16_t>();
data_block.data.data_length = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
data_block.data.pause_after_block = get<uint16_t>(file_);
data_block.data.data_length = get<uint16_t>(file_);
if(!data_block.data.data_length) return;
const uint8_t first_byte = file_.get();
@@ -240,15 +246,16 @@ void TZX::Serialiser::get_standard_speed_data_block() {
void TZX::Serialiser::get_turbo_speed_data_block() {
DataBlock data_block;
data_block.length_of_pilot_pulse = file_.get_le<uint16_t>();
data_block.length_of_sync_first_pulse = file_.get_le<uint16_t>();
data_block.length_of_sync_second_pulse = file_.get_le<uint16_t>();
data_block.data.length_of_zero_bit_pulse = file_.get_le<uint16_t>();
data_block.data.length_of_one_bit_pulse = file_.get_le<uint16_t>();
data_block.length_of_pilot_tone = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
data_block.length_of_pilot_pulse = get<uint16_t>(file_);
data_block.length_of_sync_first_pulse = get<uint16_t>(file_);
data_block.length_of_sync_second_pulse = get<uint16_t>(file_);
data_block.data.length_of_zero_bit_pulse = get<uint16_t>(file_);
data_block.data.length_of_one_bit_pulse = get<uint16_t>(file_);
data_block.length_of_pilot_tone = get<uint16_t>(file_);
data_block.data.number_of_bits_in_final_byte = file_.get();
data_block.data.pause_after_block = file_.get_le<uint16_t>();
data_block.data.data_length = file_.get_le<uint32_t, 3>();
data_block.data.pause_after_block = get<uint16_t>(file_);
data_block.data.data_length = get<uint32_t, 3>(file_);
get_data_block(data_block);
}
@@ -284,28 +291,31 @@ void TZX::Serialiser::get_data(const Data &data) {
}
void TZX::Serialiser::get_pure_tone_data_block() {
const auto length_of_pulse = file_.get_le<uint16_t>();
const auto nunber_of_pulses = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
const auto length_of_pulse = get<uint16_t>(file_);
const auto nunber_of_pulses = get<uint16_t>(file_);
post_pulses(nunber_of_pulses, length_of_pulse);
}
void TZX::Serialiser::get_pure_data_block() {
Data data;
data.length_of_zero_bit_pulse = file_.get_le<uint16_t>();
data.length_of_one_bit_pulse = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
data.length_of_zero_bit_pulse = get<uint16_t>(file_);
data.length_of_one_bit_pulse = get<uint16_t>(file_);
data.number_of_bits_in_final_byte = file_.get();
data.pause_after_block = file_.get_le<uint16_t>();
data.data_length = file_.get_le<uint32_t, 3>();
data.pause_after_block = get<uint16_t>(file_);
data.data_length = get<uint32_t, 3>(file_);
get_data(data);
}
void TZX::Serialiser::get_direct_recording_block() {
const Storage::Time length_per_sample(unsigned(file_.get_le<uint16_t>()), StandardTZXClock);
const auto pause_after_block = file_.get_le<uint16_t>();
using FileUtils::LittleEndian::get;
const Storage::Time length_per_sample(unsigned(get<uint16_t>(file_)), StandardTZXClock);
const auto pause_after_block = get<uint16_t>(file_);
const uint8_t used_bits_in_final_byte = std::clamp<uint8_t>(file_.get(), 1, 8);
const auto length_of_data = file_.get_le<uint32_t, 3>();
const auto length_of_data = get<uint32_t, 3>(file_);
unsigned int bits_at_level = 0;
bool level = false;
@@ -341,12 +351,12 @@ void TZX::Serialiser::get_direct_recording_block() {
void TZX::Serialiser::get_pulse_sequence() {
uint8_t number_of_pulses = file_.get();
while(number_of_pulses--) {
post_pulse(file_.get_le<uint16_t>());
post_pulse(FileUtils::get<std::endian::little, uint16_t>(file_));
}
}
void TZX::Serialiser::get_pause() {
const auto duration = file_.get_le<uint16_t>();
const auto duration = FileUtils::get<std::endian::little, uint16_t>(file_);
if(!duration) {
// TODO (maybe): post a 'pause the tape' suggestion
} else {
@@ -361,14 +371,15 @@ void TZX::Serialiser::get_set_signal_level() {
}
void TZX::Serialiser::get_kansas_city_block() {
auto block_length = file_.get_le<uint32_t>();
using FileUtils::LittleEndian::get;
auto block_length = get<uint32_t>(file_);
const auto pause_after_block = file_.get_le<uint16_t>();
const auto pilot_pulse_duration = file_.get_le<uint16_t>();
const auto pilot_length = file_.get_le<uint16_t>();
const auto pause_after_block = get<uint16_t>(file_);
const auto pilot_pulse_duration = get<uint16_t>(file_);
const auto pilot_length = get<uint16_t>(file_);
uint16_t pulse_durations[2];
pulse_durations[0] = file_.get_le<uint16_t>();
pulse_durations[1] = file_.get_le<uint16_t>();
pulse_durations[0] = get<uint16_t>(file_);
pulse_durations[1] = get<uint16_t>(file_);
const uint8_t packed_pulse_counts = file_.get();
const unsigned int pulse_counts[2] = {
unsigned((((packed_pulse_counts >> 4) - 1) & 15) + 1),
@@ -452,12 +463,12 @@ void TZX::Serialiser::ignore_group_end() {
}
void TZX::Serialiser::ignore_jump_to_block() {
const auto target = file_.get_le<uint16_t>();
const auto target = FileUtils::get<std::endian::little, uint16_t>(file_);
(void)target;
}
void TZX::Serialiser::ignore_loop_start() {
const auto number_of_repetitions = file_.get_le<uint16_t>();
const auto number_of_repetitions = FileUtils::get<std::endian::little, uint16_t>(file_);
(void)number_of_repetitions;
}
@@ -465,7 +476,7 @@ void TZX::Serialiser::ignore_loop_end() {
}
void TZX::Serialiser::ignore_call_sequence() {
const auto number_of_entries = file_.get_le<uint16_t>();
const auto number_of_entries = FileUtils::get<std::endian::little, uint16_t>(file_);
file_.seek(number_of_entries * sizeof(uint16_t), Whence::CUR);
}
@@ -473,7 +484,7 @@ void TZX::Serialiser::ignore_return_from_sequence() {
}
void TZX::Serialiser::ignore_select_block() {
const auto length_of_block = file_.get_le<uint16_t>();
const auto length_of_block = FileUtils::get<std::endian::little, uint16_t>(file_);
file_.seek(length_of_block, Whence::CUR);
}
@@ -483,7 +494,7 @@ void TZX::Serialiser::ignore_stop_tape_if_in_48kb_mode() {
void TZX::Serialiser::ignore_custom_info_block() {
file_.seek(0x10, Whence::CUR);
const auto length = file_.get_le<uint32_t>();
const auto length = FileUtils::get<std::endian::little, uint32_t>(file_);
file_.seek(length, Whence::CUR);
}
@@ -502,7 +513,7 @@ void TZX::Serialiser::ignore_message_block() {
}
void TZX::Serialiser::ignore_archive_info() {
const auto length = file_.get_le<uint16_t>();
const auto length = FileUtils::get<std::endian::little, uint16_t>(file_);
file_.seek(length, Whence::CUR);
}
+1 -1
View File
@@ -56,7 +56,7 @@ PRG::PRG(const std::filesystem::path &path) : path_(path) {
if(file.stats().st_size >= 65538 || file.stats().st_size < 3)
throw ErrorBadFormat;
load_address_ = file.get_le<uint16_t>();
load_address_ = FileUtils::get<std::endian::little, uint16_t>(file);
length_ = uint16_t(file.stats().st_size - 2);
if(load_address_ + length_ >= 65536)
+2 -2
View File
@@ -27,7 +27,7 @@ ZXSpectrumTAP::ZXSpectrumTAP(const std::filesystem::path &path) : path_(path) {
// To consider: could also check those blocks of type 0
// and type ff for valid checksums?
while(file.tell() != file.stats().st_size) {
const uint16_t block_length = file.get_le<uint16_t>();
const uint16_t block_length = FileUtils::get<std::endian::little, uint16_t>(file);
if(file.eof() || file.tell() + block_length > file.stats().st_size) {
throw ErrorNotZXSpectrumTAP;
}
@@ -121,7 +121,7 @@ void ZXSpectrumTAP::Serialiser::read_next_block() {
if(file_.tell() == file_.stats().st_size) {
phase_ = Phase::Gap;
} else {
block_length_ = file_.get_le<uint16_t>();
block_length_ = FileUtils::get<std::endian::little, uint16_t>(file_);
data_byte_ = block_type_ = file_.get();
phase_ = Phase::PilotTone;
}